Root/
Source at commit 973dc02c4c8e6c099225c3b6a9e3e648e3930f48 created 13 years 10 months ago. By Maarten ter Huurne, jz4740-slcd-fb: Compute the number of pixels instead of hardcoding it. | |
---|---|
1 | /* |
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> |
3 | * Copyright (C) 2010, Maarten ter Huurne <maarten@treewalker.org> |
4 | * JZ4720/JZ4740 SoC LCD framebuffer driver |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the |
8 | * Free Software Foundation; either version 2 of the License, or (at your |
9 | * option) any later version. |
10 | * |
11 | * You should have received a copy of the GNU General Public License along |
12 | * with this program; if not, write to the Free Software Foundation, Inc., |
13 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
14 | * |
15 | */ |
16 | |
17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> |
19 | #include <linux/mutex.h> |
20 | #include <linux/platform_device.h> |
21 | |
22 | #include <linux/clk.h> |
23 | #include <linux/delay.h> |
24 | |
25 | #include <linux/console.h> |
26 | #include <linux/fb.h> |
27 | |
28 | #include <linux/dma-mapping.h> |
29 | |
30 | #include <linux/jz4740_fb.h> |
31 | #include <asm/mach-jz4740/dma.h> |
32 | #include <asm/mach-jz4740/gpio.h> |
33 | |
34 | #include "jz4740_lcd.h" |
35 | #include "jz4740_slcd.h" |
36 | |
37 | struct jzfb_framedesc { |
38 | uint32_t next; |
39 | uint32_t addr; |
40 | uint32_t id; |
41 | uint32_t cmd; |
42 | } __attribute__((packed)); |
43 | |
44 | static struct fb_fix_screeninfo jzfb_fix __devinitdata = { |
45 | .id = "JZ4740 SLCD FB", |
46 | .type = FB_TYPE_PACKED_PIXELS, |
47 | .visual = FB_VISUAL_TRUECOLOR, |
48 | .xpanstep = 0, |
49 | .ypanstep = 0, |
50 | .ywrapstep = 0, |
51 | .accel = FB_ACCEL_NONE, |
52 | }; |
53 | |
54 | const static struct jz_gpio_bulk_request jz_slcd_ctrl_pins[] = { |
55 | JZ_GPIO_BULK_PIN(LCD_PCLK), |
56 | JZ_GPIO_BULK_PIN(SLCD_RS), |
57 | JZ_GPIO_BULK_PIN(SLCD_CS), |
58 | }; |
59 | |
60 | const static struct jz_gpio_bulk_request jz_slcd_data_pins[] = { |
61 | JZ_GPIO_BULK_PIN(LCD_DATA0), |
62 | JZ_GPIO_BULK_PIN(LCD_DATA1), |
63 | JZ_GPIO_BULK_PIN(LCD_DATA2), |
64 | JZ_GPIO_BULK_PIN(LCD_DATA3), |
65 | JZ_GPIO_BULK_PIN(LCD_DATA4), |
66 | JZ_GPIO_BULK_PIN(LCD_DATA5), |
67 | JZ_GPIO_BULK_PIN(LCD_DATA6), |
68 | JZ_GPIO_BULK_PIN(LCD_DATA7), |
69 | JZ_GPIO_BULK_PIN(LCD_DATA8), |
70 | JZ_GPIO_BULK_PIN(LCD_DATA9), |
71 | JZ_GPIO_BULK_PIN(LCD_DATA10), |
72 | JZ_GPIO_BULK_PIN(LCD_DATA11), |
73 | JZ_GPIO_BULK_PIN(LCD_DATA12), |
74 | JZ_GPIO_BULK_PIN(LCD_DATA13), |
75 | JZ_GPIO_BULK_PIN(LCD_DATA14), |
76 | JZ_GPIO_BULK_PIN(LCD_DATA15), |
77 | JZ_GPIO_BULK_PIN(LCD_DATA16), |
78 | JZ_GPIO_BULK_PIN(LCD_DATA17), |
79 | }; |
80 | |
81 | static unsigned int jzfb_num_ctrl_pins(struct jzfb *jzfb) |
82 | { |
83 | return ARRAY_SIZE(jz_slcd_ctrl_pins); |
84 | } |
85 | |
86 | static unsigned int jzfb_num_data_pins(struct jzfb *jzfb) |
87 | { |
88 | switch (jzfb->pdata->lcd_type) { |
89 | case JZ_LCD_TYPE_SMART_PARALLEL_8_BIT: |
90 | return 8; |
91 | case JZ_LCD_TYPE_SMART_PARALLEL_16_BIT: |
92 | return 16; |
93 | case JZ_LCD_TYPE_SMART_PARALLEL_18_BIT: |
94 | return 18; |
95 | default: |
96 | return 0; |
97 | } |
98 | } |
99 | |
100 | static void jzfb_free_gpio_pins(struct jzfb *jzfb) |
101 | { |
102 | jz_gpio_bulk_free(jz_slcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); |
103 | if (jzfb->pdata->lcd_type & (1 << 6)) { |
104 | /* serial */ |
105 | jz_gpio_bulk_free(&jz_slcd_data_pins[15], 1); |
106 | } else { |
107 | /* parallel */ |
108 | jz_gpio_bulk_free(jz_slcd_data_pins, |
109 | jzfb_num_data_pins(jzfb)); |
110 | } |
111 | } |
112 | |
113 | static int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green, |
114 | unsigned blue, unsigned transp, struct fb_info *fb) |
115 | { |
116 | if (regno >= 16) |
117 | return -EINVAL; |
118 | |
119 | red = (red * ((1 << fb->var.red.length ) - 1)) / ((1 << 16) - 1); |
120 | green = (green * ((1 << fb->var.green.length) - 1)) / ((1 << 16) - 1); |
121 | blue = (blue * ((1 << fb->var.blue.length ) - 1)) / ((1 << 16) - 1); |
122 | |
123 | ((uint32_t *)fb->pseudo_palette)[regno] = |
124 | (red << fb->var.red.offset ) | |
125 | (green << fb->var.green.offset) | |
126 | (blue << fb->var.blue.offset ); |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | static int jzfb_get_controller_bpp(struct jzfb *jzfb) |
132 | { |
133 | switch (jzfb->pdata->bpp) { |
134 | case 18: |
135 | case 24: |
136 | return 32; |
137 | case 15: |
138 | return 16; |
139 | default: |
140 | return jzfb->pdata->bpp; |
141 | } |
142 | } |
143 | |
144 | static struct fb_videomode *jzfb_get_mode(struct jzfb *jzfb, struct fb_var_screeninfo *var) |
145 | { |
146 | size_t i; |
147 | struct fb_videomode *mode = jzfb->pdata->modes; |
148 | |
149 | for (i = 0; i < jzfb->pdata->num_modes; ++i, ++mode) { |
150 | if (mode->xres == var->xres && mode->yres == var->yres) |
151 | return mode; |
152 | } |
153 | |
154 | return NULL; |
155 | } |
156 | |
157 | static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb) |
158 | { |
159 | struct jzfb *jzfb = fb->par; |
160 | struct fb_videomode *mode; |
161 | |
162 | if (var->bits_per_pixel != jzfb_get_controller_bpp(jzfb) && |
163 | var->bits_per_pixel != jzfb->pdata->bpp) |
164 | return -EINVAL; |
165 | |
166 | mode = jzfb_get_mode(jzfb, var); |
167 | if (mode == NULL) |
168 | return -EINVAL; |
169 | |
170 | fb_videomode_to_var(var, mode); |
171 | |
172 | switch (jzfb->pdata->bpp) { |
173 | case 8: |
174 | break; |
175 | case 15: |
176 | var->red.offset = 10; |
177 | var->red.length = 5; |
178 | var->green.offset = 5; |
179 | var->green.length = 5; |
180 | var->blue.offset = 0; |
181 | var->blue.length = 5; |
182 | break; |
183 | case 16: |
184 | var->red.offset = 11; |
185 | var->red.length = 5; |
186 | var->green.offset = 5; |
187 | var->green.length = 6; |
188 | var->blue.offset = 0; |
189 | var->blue.length = 5; |
190 | break; |
191 | case 18: |
192 | var->red.offset = 16; |
193 | var->red.length = 6; |
194 | var->green.offset = 8; |
195 | var->green.length = 6; |
196 | var->blue.offset = 0; |
197 | var->blue.length = 6; |
198 | var->bits_per_pixel = 32; |
199 | break; |
200 | case 32: |
201 | case 24: |
202 | var->transp.offset = 24; |
203 | var->transp.length = 8; |
204 | var->red.offset = 16; |
205 | var->red.length = 8; |
206 | var->green.offset = 8; |
207 | var->green.length = 8; |
208 | var->blue.offset = 0; |
209 | var->blue.length = 8; |
210 | var->bits_per_pixel = 32; |
211 | break; |
212 | default: |
213 | break; |
214 | } |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static void jzfb_disable_dma(struct jzfb *jzfb) |
220 | { |
221 | jz4740_dma_disable(jzfb->dma); |
222 | while (readb(jzfb->base + JZ_REG_SLCD_STATE) & SLCD_STATE_BUSY); |
223 | writeb(readb(jzfb->base + JZ_REG_SLCD_CTRL) & ~SLCD_CTRL_DMA_EN, |
224 | jzfb->base + JZ_REG_SLCD_CTRL); |
225 | } |
226 | |
227 | static struct jz4740_dma_config jzfb_slcd_dma_config = { |
228 | .src_width = JZ4740_DMA_WIDTH_32BIT, |
229 | .dst_width = JZ4740_DMA_WIDTH_16BIT, |
230 | .transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE, |
231 | .request_type = JZ4740_DMA_TYPE_SLCD, |
232 | .flags = JZ4740_DMA_SRC_AUTOINC, |
233 | .mode = JZ4740_DMA_MODE_BLOCK, |
234 | }; |
235 | |
236 | static void jzfb_upload_frame_dma(struct jzfb *jzfb) |
237 | { |
238 | jz4740_dma_set_src_addr(jzfb->dma, jzfb->vidmem_phys); |
239 | jz4740_dma_set_dst_addr(jzfb->dma, |
240 | CPHYSADDR(jzfb->base + JZ_REG_SLCD_FIFO)); |
241 | jz4740_dma_set_transfer_count(jzfb->dma, |
242 | jzfb->fb->mode->xres * jzfb->fb->mode->yres * |
243 | (jzfb_get_controller_bpp(jzfb) >> 3) |
244 | ); |
245 | |
246 | while (readb(jzfb->base + JZ_REG_SLCD_STATE) & SLCD_STATE_BUSY); |
247 | writeb(readb(jzfb->base + JZ_REG_SLCD_CTRL) | SLCD_CTRL_DMA_EN, |
248 | jzfb->base + JZ_REG_SLCD_CTRL); |
249 | jz4740_dma_enable(jzfb->dma); |
250 | } |
251 | |
252 | static void jzfb_upload_frame_cpu(struct jzfb *jzfb) |
253 | { |
254 | const int num_pixels = jzfb->fb->mode->xres * jzfb->fb->mode->yres; |
255 | uint16_t *p = jzfb->vidmem; |
256 | int i; |
257 | |
258 | jzfb_disable_dma(jzfb); |
259 | for (i = 0; i < num_pixels; i++) { |
260 | uint16_t rgb = *p++; |
261 | while (readb(jzfb->base + JZ_REG_SLCD_STATE) & SLCD_STATE_BUSY); |
262 | writel(SLCD_DATA_RS_DATA | rgb, jzfb->base + JZ_REG_SLCD_DATA); |
263 | } |
264 | } |
265 | |
266 | static void jzfb_refresh_work(struct work_struct *work) |
267 | { |
268 | struct jzfb *jzfb = container_of(work, struct jzfb, refresh_work.work); |
269 | |
270 | mutex_lock(&jzfb->lock); |
271 | if (jzfb->is_enabled) { |
272 | if (1) { |
273 | jzfb_upload_frame_dma(jzfb); |
274 | /* The DMA complete callback will reschedule. */ |
275 | } else { |
276 | jzfb_upload_frame_cpu(jzfb); |
277 | schedule_delayed_work(&jzfb->refresh_work, HZ / 10); |
278 | } |
279 | } |
280 | mutex_unlock(&jzfb->lock); |
281 | } |
282 | |
283 | static void jzfb_refresh_work_complete( |
284 | struct jz4740_dma_chan *dma, int res, void *dev) |
285 | { |
286 | struct jzfb *jzfb = dev_get_drvdata(dev); |
287 | // TODO: Stick to refresh rate in mode description. |
288 | int interval = HZ / 60; |
289 | |
290 | schedule_delayed_work(&jzfb->refresh_work, interval); |
291 | } |
292 | |
293 | static int jzfb_set_par(struct fb_info *info) |
294 | { |
295 | struct jzfb *jzfb = info->par; |
296 | struct fb_var_screeninfo *var = &info->var; |
297 | struct fb_videomode *mode; |
298 | uint16_t slcd_cfg; |
299 | |
300 | mode = jzfb_get_mode(jzfb, var); |
301 | if (mode == NULL) |
302 | return -EINVAL; |
303 | |
304 | info->mode = mode; |
305 | |
306 | slcd_cfg = SLCD_CFG_BURST_8_WORD; |
307 | /* command size */ |
308 | slcd_cfg |= (jzfb->pdata->lcd_type & 3) << SLCD_CFG_CWIDTH_BIT; |
309 | /* data size */ |
310 | if (jzfb->pdata->lcd_type & (1 << 6)) { |
311 | /* serial */ |
312 | unsigned int num_bits; |
313 | switch (jzfb->pdata->lcd_type) { |
314 | case JZ_LCD_TYPE_SMART_SERIAL_8_BIT: |
315 | slcd_cfg |= SLCD_CFG_DWIDTH_8_x1; |
316 | num_bits = 8; |
317 | break; |
318 | case JZ_LCD_TYPE_SMART_SERIAL_16_BIT: |
319 | slcd_cfg |= SLCD_CFG_DWIDTH_16; |
320 | num_bits = 16; |
321 | break; |
322 | case JZ_LCD_TYPE_SMART_SERIAL_18_BIT: |
323 | slcd_cfg |= SLCD_CFG_DWIDTH_18; |
324 | num_bits = 18; |
325 | break; |
326 | default: |
327 | num_bits = 0; |
328 | break; |
329 | } |
330 | if (num_bits != jzfb->pdata->bpp) { |
331 | dev_err(&jzfb->pdev->dev, |
332 | "Data size (%d) does not match bpp (%d)\n", |
333 | num_bits, jzfb->pdata->bpp); |
334 | } |
335 | slcd_cfg |= SLCD_CFG_TYPE_SERIAL; |
336 | } else { |
337 | /* parallel */ |
338 | switch (jzfb->pdata->bpp) { |
339 | case 8: |
340 | slcd_cfg |= SLCD_CFG_DWIDTH_8_x1; |
341 | break; |
342 | case 15: |
343 | case 16: |
344 | switch (jzfb->pdata->lcd_type) { |
345 | case JZ_LCD_TYPE_SMART_PARALLEL_8_BIT: |
346 | slcd_cfg |= SLCD_CFG_DWIDTH_8_x2; |
347 | break; |
348 | default: |
349 | slcd_cfg |= SLCD_CFG_DWIDTH_16; |
350 | break; |
351 | } |
352 | break; |
353 | case 18: |
354 | switch (jzfb->pdata->lcd_type) { |
355 | case JZ_LCD_TYPE_SMART_PARALLEL_8_BIT: |
356 | slcd_cfg |= SLCD_CFG_DWIDTH_8_x3; |
357 | break; |
358 | case JZ_LCD_TYPE_SMART_PARALLEL_16_BIT: |
359 | slcd_cfg |= SLCD_CFG_DWIDTH_9_x2; |
360 | break; |
361 | case JZ_LCD_TYPE_SMART_PARALLEL_18_BIT: |
362 | slcd_cfg |= SLCD_CFG_DWIDTH_18; |
363 | break; |
364 | default: |
365 | break; |
366 | } |
367 | break; |
368 | case 24: |
369 | slcd_cfg |= SLCD_CFG_DWIDTH_8_x3; |
370 | break; |
371 | default: |
372 | dev_err(&jzfb->pdev->dev, |
373 | "Unsupported value for bpp: %d\n", |
374 | jzfb->pdata->bpp); |
375 | } |
376 | slcd_cfg |= SLCD_CFG_TYPE_PARALLEL; |
377 | } |
378 | if (!jzfb->pdata->chip_select_active_low) |
379 | slcd_cfg |= SLCD_CFG_CS_ACTIVE_HIGH; |
380 | if (!jzfb->pdata->register_select_active_low) |
381 | slcd_cfg |= SLCD_CFG_RS_CMD_HIGH; |
382 | if (!jzfb->pdata->pixclk_falling_edge) |
383 | slcd_cfg |= SLCD_CFG_CLK_ACTIVE_RISING; |
384 | |
385 | #if 0 |
386 | // TODO(MtH): Compute rate from refresh or vice versa. |
387 | if (mode->pixclock) { |
388 | rate = PICOS2KHZ(mode->pixclock) * 1000; |
389 | mode->refresh = rate / vt / ht; |
390 | } else { |
391 | if (jzfb->pdata->lcd_type == JZ_LCD_TYPE_8BIT_SERIAL) |
392 | rate = mode->refresh * (vt + 2 * mode->xres) * ht; |
393 | else |
394 | rate = mode->refresh * vt * ht; |
395 | |
396 | mode->pixclock = KHZ2PICOS(rate / 1000); |
397 | } |
398 | #endif |
399 | |
400 | mutex_lock(&jzfb->lock); |
401 | if (!jzfb->is_enabled) |
402 | clk_enable(jzfb->ldclk); |
403 | |
404 | // TODO(MtH): We should not change config while DMA might be running. |
405 | writew(slcd_cfg, jzfb->base + JZ_REG_SLCD_CFG); |
406 | |
407 | if (!jzfb->is_enabled) |
408 | clk_disable(jzfb->ldclk); |
409 | mutex_unlock(&jzfb->lock); |
410 | |
411 | // TODO(MtH): Use maximum transfer speed that panel can handle. |
412 | // ILI9325 can do 10 MHz. |
413 | clk_set_rate(jzfb->lpclk, 16800000); |
414 | clk_set_rate(jzfb->ldclk, 42000000); |
415 | |
416 | return 0; |
417 | } |
418 | |
419 | static void jzfb_enable(struct jzfb *jzfb) |
420 | { |
421 | uint32_t ctrl; |
422 | |
423 | clk_enable(jzfb->ldclk); |
424 | |
425 | jz_gpio_bulk_resume(jz_slcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); |
426 | if (jzfb->pdata->lcd_type & (1 << 6)) { |
427 | /* serial */ |
428 | jz_gpio_bulk_resume(&jz_slcd_data_pins[15], 1); |
429 | } else { |
430 | /* parallel */ |
431 | jz_gpio_bulk_resume(jz_slcd_data_pins, |
432 | jzfb_num_data_pins(jzfb)); |
433 | } |
434 | jzfb_disable_dma(jzfb); |
435 | jzfb->panel->enable(jzfb); |
436 | |
437 | // TODO: SLCD equivalents: |
438 | #if 0 |
439 | writel(0, jzfb->base + JZ_REG_LCD_STATE); |
440 | |
441 | writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); |
442 | #endif |
443 | |
444 | ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); |
445 | ctrl |= JZ_LCD_CTRL_ENABLE; |
446 | ctrl &= ~JZ_LCD_CTRL_DISABLE; |
447 | writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); |
448 | |
449 | schedule_delayed_work(&jzfb->refresh_work, 0); |
450 | } |
451 | |
452 | static void jzfb_disable(struct jzfb *jzfb) |
453 | { |
454 | /* It is safe but wasteful to call refresh_work() while disabled. */ |
455 | cancel_delayed_work(&jzfb->refresh_work); |
456 | |
457 | /* Abort any DMA transfer that might be in progress and allow direct |
458 | writes to the panel. */ |
459 | jzfb_disable_dma(jzfb); |
460 | |
461 | jzfb->panel->disable(jzfb); |
462 | jz_gpio_bulk_suspend(jz_slcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); |
463 | if (jzfb->pdata->lcd_type & (1 << 6)) { |
464 | /* serial */ |
465 | jz_gpio_bulk_suspend(&jz_slcd_data_pins[15], 1); |
466 | } else { |
467 | /* parallel */ |
468 | jz_gpio_bulk_suspend(jz_slcd_data_pins, |
469 | jzfb_num_data_pins(jzfb)); |
470 | } |
471 | |
472 | clk_disable(jzfb->ldclk); |
473 | } |
474 | |
475 | static int jzfb_blank(int blank_mode, struct fb_info *info) |
476 | { |
477 | struct jzfb* jzfb = info->par; |
478 | int new_enabled = (blank_mode == FB_BLANK_UNBLANK); |
479 | |
480 | mutex_lock(&jzfb->lock); |
481 | if (new_enabled) { |
482 | if (!jzfb->is_enabled) |
483 | jzfb_enable(jzfb); |
484 | } else { |
485 | if (jzfb->is_enabled) |
486 | jzfb_disable(jzfb); |
487 | } |
488 | jzfb->is_enabled = new_enabled; |
489 | mutex_unlock(&jzfb->lock); |
490 | |
491 | return 0; |
492 | } |
493 | |
494 | static int jzfb_alloc_devmem(struct jzfb *jzfb) |
495 | { |
496 | int max_videosize = 0; |
497 | struct fb_videomode *mode = jzfb->pdata->modes; |
498 | void *page; |
499 | int i; |
500 | |
501 | for (i = 0; i < jzfb->pdata->num_modes; ++mode, ++i) { |
502 | if (max_videosize < mode->xres * mode->yres) |
503 | max_videosize = mode->xres * mode->yres; |
504 | } |
505 | |
506 | max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3; |
507 | |
508 | jzfb->framedesc = dma_alloc_coherent(&jzfb->pdev->dev, |
509 | sizeof(*jzfb->framedesc), |
510 | &jzfb->framedesc_phys, GFP_KERNEL); |
511 | |
512 | if (!jzfb->framedesc) |
513 | return -ENOMEM; |
514 | |
515 | jzfb->vidmem_size = PAGE_ALIGN(max_videosize); |
516 | jzfb->vidmem = dma_alloc_coherent(&jzfb->pdev->dev, |
517 | jzfb->vidmem_size, |
518 | &jzfb->vidmem_phys, GFP_KERNEL); |
519 | |
520 | if (!jzfb->vidmem) |
521 | goto err_free_framedesc; |
522 | |
523 | for (page = jzfb->vidmem; |
524 | page < jzfb->vidmem + PAGE_ALIGN(jzfb->vidmem_size); |
525 | page += PAGE_SIZE) { |
526 | SetPageReserved(virt_to_page(page)); |
527 | } |
528 | |
529 | jzfb->framedesc->next = jzfb->framedesc_phys; |
530 | jzfb->framedesc->addr = jzfb->vidmem_phys; |
531 | jzfb->framedesc->id = 0xdeafbead; |
532 | jzfb->framedesc->cmd = 0; |
533 | jzfb->framedesc->cmd |= max_videosize / 4; |
534 | |
535 | return 0; |
536 | |
537 | err_free_framedesc: |
538 | dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), |
539 | jzfb->framedesc, jzfb->framedesc_phys); |
540 | return -ENOMEM; |
541 | } |
542 | |
543 | static void jzfb_free_devmem(struct jzfb *jzfb) |
544 | { |
545 | dma_free_coherent(&jzfb->pdev->dev, jzfb->vidmem_size, |
546 | jzfb->vidmem, jzfb->vidmem_phys); |
547 | dma_free_coherent(&jzfb->pdev->dev, sizeof(*jzfb->framedesc), |
548 | jzfb->framedesc, jzfb->framedesc_phys); |
549 | } |
550 | |
551 | static struct fb_ops jzfb_ops = { |
552 | .owner = THIS_MODULE, |
553 | .fb_check_var = jzfb_check_var, |
554 | .fb_set_par = jzfb_set_par, |
555 | .fb_setcolreg = jzfb_setcolreg, |
556 | .fb_blank = jzfb_blank, |
557 | .fb_fillrect = sys_fillrect, |
558 | .fb_copyarea = sys_copyarea, |
559 | .fb_imageblit = sys_imageblit, |
560 | }; |
561 | |
562 | static int __devinit jzfb_probe(struct platform_device *pdev) |
563 | { |
564 | int ret; |
565 | struct jzfb *jzfb; |
566 | struct fb_info *fb; |
567 | struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data; |
568 | struct resource *mem; |
569 | |
570 | if (!pdata) { |
571 | dev_err(&pdev->dev, "Missing platform data\n"); |
572 | return -ENOENT; |
573 | } |
574 | |
575 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
576 | |
577 | if (!mem) { |
578 | dev_err(&pdev->dev, "Failed to get register memory resource\n"); |
579 | return -ENOENT; |
580 | } |
581 | |
582 | mem = request_mem_region(mem->start, resource_size(mem), pdev->name); |
583 | |
584 | if (!mem) { |
585 | dev_err(&pdev->dev, "Failed to request register memory region\n"); |
586 | return -EBUSY; |
587 | } |
588 | |
589 | fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev); |
590 | |
591 | if (!fb) { |
592 | dev_err(&pdev->dev, "Failed to allocate framebuffer device\n"); |
593 | ret = -ENOMEM; |
594 | goto err_release_mem_region; |
595 | } |
596 | |
597 | fb->fbops = &jzfb_ops; |
598 | fb->flags = FBINFO_DEFAULT; |
599 | |
600 | jzfb = fb->par; |
601 | jzfb->pdev = pdev; |
602 | jzfb->pdata = pdata; |
603 | jzfb->mem = mem; |
604 | |
605 | jzfb->dma = jz4740_dma_request(&pdev->dev, dev_name(&pdev->dev)); |
606 | if (!jzfb->dma) { |
607 | dev_err(&pdev->dev, "Failed to get DMA channel\n"); |
608 | ret = -EBUSY; |
609 | goto err_framebuffer_release; |
610 | } |
611 | jz4740_dma_configure(jzfb->dma, &jzfb_slcd_dma_config); |
612 | jz4740_dma_set_complete_cb(jzfb->dma, &jzfb_refresh_work_complete); |
613 | |
614 | jzfb->ldclk = clk_get(&pdev->dev, "lcd"); |
615 | if (IS_ERR(jzfb->ldclk)) { |
616 | ret = PTR_ERR(jzfb->ldclk); |
617 | dev_err(&pdev->dev, "Failed to get lcd clock: %d\n", ret); |
618 | goto err_free_dma; |
619 | } |
620 | |
621 | jzfb->lpclk = clk_get(&pdev->dev, "lcd_pclk"); |
622 | if (IS_ERR(jzfb->lpclk)) { |
623 | ret = PTR_ERR(jzfb->lpclk); |
624 | dev_err(&pdev->dev, "Failed to get lcd pixel clock: %d\n", ret); |
625 | goto err_put_ldclk; |
626 | } |
627 | |
628 | jzfb->base = ioremap(mem->start, resource_size(mem)); |
629 | |
630 | if (!jzfb->base) { |
631 | dev_err(&pdev->dev, "Failed to ioremap register memory region\n"); |
632 | ret = -EBUSY; |
633 | goto err_put_lpclk; |
634 | } |
635 | |
636 | platform_set_drvdata(pdev, jzfb); |
637 | |
638 | fb_videomode_to_modelist(pdata->modes, pdata->num_modes, |
639 | &fb->modelist); |
640 | fb->mode = pdata->modes; |
641 | |
642 | fb_videomode_to_var(&fb->var, fb->mode); |
643 | fb->var.bits_per_pixel = pdata->bpp; |
644 | jzfb_check_var(&fb->var, fb); |
645 | |
646 | ret = jzfb_alloc_devmem(jzfb); |
647 | if (ret) { |
648 | dev_err(&pdev->dev, "Failed to allocate video memory\n"); |
649 | goto err_iounmap; |
650 | } |
651 | |
652 | fb->fix = jzfb_fix; |
653 | fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8; |
654 | fb->fix.mmio_start = mem->start; |
655 | fb->fix.mmio_len = resource_size(mem); |
656 | fb->fix.smem_start = jzfb->vidmem_phys; |
657 | fb->fix.smem_len = fb->fix.line_length * fb->var.yres; |
658 | fb->screen_base = jzfb->vidmem; |
659 | fb->pseudo_palette = jzfb->pseudo_palette; |
660 | |
661 | fb_alloc_cmap(&fb->cmap, 256, 0); |
662 | |
663 | mutex_init(&jzfb->lock); |
664 | |
665 | clk_enable(jzfb->ldclk); |
666 | jzfb->is_enabled = 1; |
667 | |
668 | writel(JZ_LCD_CFG_SLCD, jzfb->base + JZ_REG_LCD_CFG); |
669 | writeb(0, jzfb->base + JZ_REG_SLCD_CTRL); |
670 | |
671 | jzfb_set_par(fb); |
672 | |
673 | jz_gpio_bulk_request(jz_slcd_ctrl_pins, jzfb_num_ctrl_pins(jzfb)); |
674 | if (jzfb->pdata->lcd_type & (1 << 6)) { |
675 | /* serial */ |
676 | jz_gpio_bulk_request(&jz_slcd_data_pins[15], 1); |
677 | } else { |
678 | /* parallel */ |
679 | jz_gpio_bulk_request(jz_slcd_data_pins, |
680 | jzfb_num_data_pins(jzfb)); |
681 | } |
682 | |
683 | jzfb->panel = jz_slcd_panels_probe(jzfb); |
684 | if (!jzfb->panel) { |
685 | dev_err(&pdev->dev, "Failed to find panel driver\n"); |
686 | ret = -ENOENT; |
687 | goto err_free_devmem; |
688 | } |
689 | jzfb_disable_dma(jzfb); |
690 | jzfb->panel->init(jzfb); |
691 | jzfb->panel->enable(jzfb); |
692 | |
693 | ret = register_framebuffer(fb); |
694 | if (ret) { |
695 | dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret); |
696 | goto err_free_panel; |
697 | } |
698 | |
699 | jzfb->fb = fb; |
700 | |
701 | INIT_DELAYED_WORK(&jzfb->refresh_work, jzfb_refresh_work); |
702 | schedule_delayed_work(&jzfb->refresh_work, 0); |
703 | |
704 | return 0; |
705 | |
706 | err_free_panel: |
707 | jzfb->panel->exit(jzfb); |
708 | err_free_devmem: |
709 | jzfb_free_gpio_pins(jzfb); |
710 | |
711 | fb_dealloc_cmap(&fb->cmap); |
712 | jzfb_free_devmem(jzfb); |
713 | err_iounmap: |
714 | iounmap(jzfb->base); |
715 | err_put_lpclk: |
716 | clk_put(jzfb->lpclk); |
717 | err_put_ldclk: |
718 | clk_put(jzfb->ldclk); |
719 | err_free_dma: |
720 | jz4740_dma_free(jzfb->dma); |
721 | err_framebuffer_release: |
722 | framebuffer_release(fb); |
723 | err_release_mem_region: |
724 | release_mem_region(mem->start, resource_size(mem)); |
725 | return ret; |
726 | } |
727 | |
728 | static int __devexit jzfb_remove(struct platform_device *pdev) |
729 | { |
730 | struct jzfb *jzfb = platform_get_drvdata(pdev); |
731 | |
732 | jzfb_blank(FB_BLANK_POWERDOWN, jzfb->fb); |
733 | |
734 | /* Blanking will prevent future refreshes from behind scheduled. |
735 | Now wait for a possible refresh in progress to finish. */ |
736 | cancel_delayed_work_sync(&jzfb->refresh_work); |
737 | |
738 | jzfb->panel->exit(jzfb); |
739 | |
740 | jzfb_free_gpio_pins(jzfb); |
741 | |
742 | // TODO: Which part of the code guarantees that a DMA transfer is |
743 | // not still in progress? |
744 | // * jz4740_dma_free() does not |
745 | // * FB_BLANK_POWERDOWN should, I think; check it |
746 | jz4740_dma_free(jzfb->dma); |
747 | |
748 | iounmap(jzfb->base); |
749 | release_mem_region(jzfb->mem->start, resource_size(jzfb->mem)); |
750 | |
751 | fb_dealloc_cmap(&jzfb->fb->cmap); |
752 | jzfb_free_devmem(jzfb); |
753 | |
754 | platform_set_drvdata(pdev, NULL); |
755 | |
756 | clk_put(jzfb->lpclk); |
757 | clk_put(jzfb->ldclk); |
758 | |
759 | framebuffer_release(jzfb->fb); |
760 | |
761 | return 0; |
762 | } |
763 | |
764 | #ifdef CONFIG_PM |
765 | |
766 | static int jzfb_suspend(struct device *dev) |
767 | { |
768 | struct jzfb *jzfb = dev_get_drvdata(dev); |
769 | |
770 | acquire_console_sem(); |
771 | fb_set_suspend(jzfb->fb, 1); |
772 | release_console_sem(); |
773 | |
774 | mutex_lock(&jzfb->lock); |
775 | if (jzfb->is_enabled) |
776 | jzfb_disable(jzfb); |
777 | mutex_unlock(&jzfb->lock); |
778 | |
779 | return 0; |
780 | } |
781 | |
782 | static int jzfb_resume(struct device *dev) |
783 | { |
784 | struct jzfb *jzfb = dev_get_drvdata(dev); |
785 | clk_enable(jzfb->ldclk); |
786 | |
787 | mutex_lock(&jzfb->lock); |
788 | if (jzfb->is_enabled) |
789 | jzfb_enable(jzfb); |
790 | mutex_unlock(&jzfb->lock); |
791 | |
792 | acquire_console_sem(); |
793 | fb_set_suspend(jzfb->fb, 0); |
794 | release_console_sem(); |
795 | |
796 | return 0; |
797 | } |
798 | |
799 | static const struct dev_pm_ops jzfb_pm_ops = { |
800 | .suspend = jzfb_suspend, |
801 | .resume = jzfb_resume, |
802 | .poweroff = jzfb_suspend, |
803 | .restore = jzfb_resume, |
804 | }; |
805 | |
806 | #define JZFB_PM_OPS (&jzfb_pm_ops) |
807 | |
808 | #else |
809 | #define JZFB_PM_OPS NULL |
810 | #endif |
811 | |
812 | static struct platform_driver jzfb_driver = { |
813 | .probe = jzfb_probe, |
814 | .remove = __devexit_p(jzfb_remove), |
815 | .driver = { |
816 | .name = "jz4740-slcd-fb", |
817 | .pm = JZFB_PM_OPS, |
818 | }, |
819 | }; |
820 | |
821 | static int __init jzfb_init(void) |
822 | { |
823 | return platform_driver_register(&jzfb_driver); |
824 | } |
825 | module_init(jzfb_init); |
826 | |
827 | static void __exit jzfb_exit(void) |
828 | { |
829 | platform_driver_unregister(&jzfb_driver); |
830 | } |
831 | module_exit(jzfb_exit); |
832 | |
833 | MODULE_LICENSE("GPL"); |
834 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>, Maarten ter Huurne <maarten@treewalker.org>"); |
835 | MODULE_DESCRIPTION("JZ4740 SoC SLCD framebuffer driver"); |
836 | MODULE_ALIAS("platform:jz4740-slcd-fb"); |
837 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9