diff options
Diffstat (limited to 'drivers/video/fbdev/mx3fb.c')
-rw-r--r-- | drivers/video/fbdev/mx3fb.c | 1630 |
1 files changed, 1630 insertions, 0 deletions
diff --git a/drivers/video/fbdev/mx3fb.c b/drivers/video/fbdev/mx3fb.c new file mode 100644 index 000000000000..142e860fb527 --- /dev/null +++ b/drivers/video/fbdev/mx3fb.c | |||
@@ -0,0 +1,1630 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 | ||
3 | * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de> | ||
4 | * | ||
5 | * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/fb.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/ioport.h> | ||
24 | #include <linux/dma-mapping.h> | ||
25 | #include <linux/dmaengine.h> | ||
26 | #include <linux/console.h> | ||
27 | #include <linux/clk.h> | ||
28 | #include <linux/mutex.h> | ||
29 | #include <linux/dma/ipu-dma.h> | ||
30 | |||
31 | #include <linux/platform_data/dma-imx.h> | ||
32 | #include <linux/platform_data/video-mx3fb.h> | ||
33 | |||
34 | #include <asm/io.h> | ||
35 | #include <asm/uaccess.h> | ||
36 | |||
37 | #define MX3FB_NAME "mx3_sdc_fb" | ||
38 | |||
39 | #define MX3FB_REG_OFFSET 0xB4 | ||
40 | |||
41 | /* SDC Registers */ | ||
42 | #define SDC_COM_CONF (0xB4 - MX3FB_REG_OFFSET) | ||
43 | #define SDC_GW_CTRL (0xB8 - MX3FB_REG_OFFSET) | ||
44 | #define SDC_FG_POS (0xBC - MX3FB_REG_OFFSET) | ||
45 | #define SDC_BG_POS (0xC0 - MX3FB_REG_OFFSET) | ||
46 | #define SDC_CUR_POS (0xC4 - MX3FB_REG_OFFSET) | ||
47 | #define SDC_PWM_CTRL (0xC8 - MX3FB_REG_OFFSET) | ||
48 | #define SDC_CUR_MAP (0xCC - MX3FB_REG_OFFSET) | ||
49 | #define SDC_HOR_CONF (0xD0 - MX3FB_REG_OFFSET) | ||
50 | #define SDC_VER_CONF (0xD4 - MX3FB_REG_OFFSET) | ||
51 | #define SDC_SHARP_CONF_1 (0xD8 - MX3FB_REG_OFFSET) | ||
52 | #define SDC_SHARP_CONF_2 (0xDC - MX3FB_REG_OFFSET) | ||
53 | |||
54 | /* Register bits */ | ||
55 | #define SDC_COM_TFT_COLOR 0x00000001UL | ||
56 | #define SDC_COM_FG_EN 0x00000010UL | ||
57 | #define SDC_COM_GWSEL 0x00000020UL | ||
58 | #define SDC_COM_GLB_A 0x00000040UL | ||
59 | #define SDC_COM_KEY_COLOR_G 0x00000080UL | ||
60 | #define SDC_COM_BG_EN 0x00000200UL | ||
61 | #define SDC_COM_SHARP 0x00001000UL | ||
62 | |||
63 | #define SDC_V_SYNC_WIDTH_L 0x00000001UL | ||
64 | |||
65 | /* Display Interface registers */ | ||
66 | #define DI_DISP_IF_CONF (0x0124 - MX3FB_REG_OFFSET) | ||
67 | #define DI_DISP_SIG_POL (0x0128 - MX3FB_REG_OFFSET) | ||
68 | #define DI_SER_DISP1_CONF (0x012C - MX3FB_REG_OFFSET) | ||
69 | #define DI_SER_DISP2_CONF (0x0130 - MX3FB_REG_OFFSET) | ||
70 | #define DI_HSP_CLK_PER (0x0134 - MX3FB_REG_OFFSET) | ||
71 | #define DI_DISP0_TIME_CONF_1 (0x0138 - MX3FB_REG_OFFSET) | ||
72 | #define DI_DISP0_TIME_CONF_2 (0x013C - MX3FB_REG_OFFSET) | ||
73 | #define DI_DISP0_TIME_CONF_3 (0x0140 - MX3FB_REG_OFFSET) | ||
74 | #define DI_DISP1_TIME_CONF_1 (0x0144 - MX3FB_REG_OFFSET) | ||
75 | #define DI_DISP1_TIME_CONF_2 (0x0148 - MX3FB_REG_OFFSET) | ||
76 | #define DI_DISP1_TIME_CONF_3 (0x014C - MX3FB_REG_OFFSET) | ||
77 | #define DI_DISP2_TIME_CONF_1 (0x0150 - MX3FB_REG_OFFSET) | ||
78 | #define DI_DISP2_TIME_CONF_2 (0x0154 - MX3FB_REG_OFFSET) | ||
79 | #define DI_DISP2_TIME_CONF_3 (0x0158 - MX3FB_REG_OFFSET) | ||
80 | #define DI_DISP3_TIME_CONF (0x015C - MX3FB_REG_OFFSET) | ||
81 | #define DI_DISP0_DB0_MAP (0x0160 - MX3FB_REG_OFFSET) | ||
82 | #define DI_DISP0_DB1_MAP (0x0164 - MX3FB_REG_OFFSET) | ||
83 | #define DI_DISP0_DB2_MAP (0x0168 - MX3FB_REG_OFFSET) | ||
84 | #define DI_DISP0_CB0_MAP (0x016C - MX3FB_REG_OFFSET) | ||
85 | #define DI_DISP0_CB1_MAP (0x0170 - MX3FB_REG_OFFSET) | ||
86 | #define DI_DISP0_CB2_MAP (0x0174 - MX3FB_REG_OFFSET) | ||
87 | #define DI_DISP1_DB0_MAP (0x0178 - MX3FB_REG_OFFSET) | ||
88 | #define DI_DISP1_DB1_MAP (0x017C - MX3FB_REG_OFFSET) | ||
89 | #define DI_DISP1_DB2_MAP (0x0180 - MX3FB_REG_OFFSET) | ||
90 | #define DI_DISP1_CB0_MAP (0x0184 - MX3FB_REG_OFFSET) | ||
91 | #define DI_DISP1_CB1_MAP (0x0188 - MX3FB_REG_OFFSET) | ||
92 | #define DI_DISP1_CB2_MAP (0x018C - MX3FB_REG_OFFSET) | ||
93 | #define DI_DISP2_DB0_MAP (0x0190 - MX3FB_REG_OFFSET) | ||
94 | #define DI_DISP2_DB1_MAP (0x0194 - MX3FB_REG_OFFSET) | ||
95 | #define DI_DISP2_DB2_MAP (0x0198 - MX3FB_REG_OFFSET) | ||
96 | #define DI_DISP2_CB0_MAP (0x019C - MX3FB_REG_OFFSET) | ||
97 | #define DI_DISP2_CB1_MAP (0x01A0 - MX3FB_REG_OFFSET) | ||
98 | #define DI_DISP2_CB2_MAP (0x01A4 - MX3FB_REG_OFFSET) | ||
99 | #define DI_DISP3_B0_MAP (0x01A8 - MX3FB_REG_OFFSET) | ||
100 | #define DI_DISP3_B1_MAP (0x01AC - MX3FB_REG_OFFSET) | ||
101 | #define DI_DISP3_B2_MAP (0x01B0 - MX3FB_REG_OFFSET) | ||
102 | #define DI_DISP_ACC_CC (0x01B4 - MX3FB_REG_OFFSET) | ||
103 | #define DI_DISP_LLA_CONF (0x01B8 - MX3FB_REG_OFFSET) | ||
104 | #define DI_DISP_LLA_DATA (0x01BC - MX3FB_REG_OFFSET) | ||
105 | |||
106 | /* DI_DISP_SIG_POL bits */ | ||
107 | #define DI_D3_VSYNC_POL_SHIFT 28 | ||
108 | #define DI_D3_HSYNC_POL_SHIFT 27 | ||
109 | #define DI_D3_DRDY_SHARP_POL_SHIFT 26 | ||
110 | #define DI_D3_CLK_POL_SHIFT 25 | ||
111 | #define DI_D3_DATA_POL_SHIFT 24 | ||
112 | |||
113 | /* DI_DISP_IF_CONF bits */ | ||
114 | #define DI_D3_CLK_IDLE_SHIFT 26 | ||
115 | #define DI_D3_CLK_SEL_SHIFT 25 | ||
116 | #define DI_D3_DATAMSK_SHIFT 24 | ||
117 | |||
118 | enum ipu_panel { | ||
119 | IPU_PANEL_SHARP_TFT, | ||
120 | IPU_PANEL_TFT, | ||
121 | }; | ||
122 | |||
123 | struct ipu_di_signal_cfg { | ||
124 | unsigned datamask_en:1; | ||
125 | unsigned clksel_en:1; | ||
126 | unsigned clkidle_en:1; | ||
127 | unsigned data_pol:1; /* true = inverted */ | ||
128 | unsigned clk_pol:1; /* true = rising edge */ | ||
129 | unsigned enable_pol:1; | ||
130 | unsigned Hsync_pol:1; /* true = active high */ | ||
131 | unsigned Vsync_pol:1; | ||
132 | }; | ||
133 | |||
134 | static const struct fb_videomode mx3fb_modedb[] = { | ||
135 | { | ||
136 | /* 240x320 @ 60 Hz */ | ||
137 | .name = "Sharp-QVGA", | ||
138 | .refresh = 60, | ||
139 | .xres = 240, | ||
140 | .yres = 320, | ||
141 | .pixclock = 185925, | ||
142 | .left_margin = 9, | ||
143 | .right_margin = 16, | ||
144 | .upper_margin = 7, | ||
145 | .lower_margin = 9, | ||
146 | .hsync_len = 1, | ||
147 | .vsync_len = 1, | ||
148 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | | ||
149 | FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT | | ||
150 | FB_SYNC_CLK_IDLE_EN, | ||
151 | .vmode = FB_VMODE_NONINTERLACED, | ||
152 | .flag = 0, | ||
153 | }, { | ||
154 | /* 240x33 @ 60 Hz */ | ||
155 | .name = "Sharp-CLI", | ||
156 | .refresh = 60, | ||
157 | .xres = 240, | ||
158 | .yres = 33, | ||
159 | .pixclock = 185925, | ||
160 | .left_margin = 9, | ||
161 | .right_margin = 16, | ||
162 | .upper_margin = 7, | ||
163 | .lower_margin = 9 + 287, | ||
164 | .hsync_len = 1, | ||
165 | .vsync_len = 1, | ||
166 | .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | | ||
167 | FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT | | ||
168 | FB_SYNC_CLK_IDLE_EN, | ||
169 | .vmode = FB_VMODE_NONINTERLACED, | ||
170 | .flag = 0, | ||
171 | }, { | ||
172 | /* 640x480 @ 60 Hz */ | ||
173 | .name = "NEC-VGA", | ||
174 | .refresh = 60, | ||
175 | .xres = 640, | ||
176 | .yres = 480, | ||
177 | .pixclock = 38255, | ||
178 | .left_margin = 144, | ||
179 | .right_margin = 0, | ||
180 | .upper_margin = 34, | ||
181 | .lower_margin = 40, | ||
182 | .hsync_len = 1, | ||
183 | .vsync_len = 1, | ||
184 | .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, | ||
185 | .vmode = FB_VMODE_NONINTERLACED, | ||
186 | .flag = 0, | ||
187 | }, { | ||
188 | /* NTSC TV output */ | ||
189 | .name = "TV-NTSC", | ||
190 | .refresh = 60, | ||
191 | .xres = 640, | ||
192 | .yres = 480, | ||
193 | .pixclock = 37538, | ||
194 | .left_margin = 38, | ||
195 | .right_margin = 858 - 640 - 38 - 3, | ||
196 | .upper_margin = 36, | ||
197 | .lower_margin = 518 - 480 - 36 - 1, | ||
198 | .hsync_len = 3, | ||
199 | .vsync_len = 1, | ||
200 | .sync = 0, | ||
201 | .vmode = FB_VMODE_NONINTERLACED, | ||
202 | .flag = 0, | ||
203 | }, { | ||
204 | /* PAL TV output */ | ||
205 | .name = "TV-PAL", | ||
206 | .refresh = 50, | ||
207 | .xres = 640, | ||
208 | .yres = 480, | ||
209 | .pixclock = 37538, | ||
210 | .left_margin = 38, | ||
211 | .right_margin = 960 - 640 - 38 - 32, | ||
212 | .upper_margin = 32, | ||
213 | .lower_margin = 555 - 480 - 32 - 3, | ||
214 | .hsync_len = 32, | ||
215 | .vsync_len = 3, | ||
216 | .sync = 0, | ||
217 | .vmode = FB_VMODE_NONINTERLACED, | ||
218 | .flag = 0, | ||
219 | }, { | ||
220 | /* TV output VGA mode, 640x480 @ 65 Hz */ | ||
221 | .name = "TV-VGA", | ||
222 | .refresh = 60, | ||
223 | .xres = 640, | ||
224 | .yres = 480, | ||
225 | .pixclock = 40574, | ||
226 | .left_margin = 35, | ||
227 | .right_margin = 45, | ||
228 | .upper_margin = 9, | ||
229 | .lower_margin = 1, | ||
230 | .hsync_len = 46, | ||
231 | .vsync_len = 5, | ||
232 | .sync = 0, | ||
233 | .vmode = FB_VMODE_NONINTERLACED, | ||
234 | .flag = 0, | ||
235 | }, | ||
236 | }; | ||
237 | |||
238 | struct mx3fb_data { | ||
239 | struct fb_info *fbi; | ||
240 | int backlight_level; | ||
241 | void __iomem *reg_base; | ||
242 | spinlock_t lock; | ||
243 | struct device *dev; | ||
244 | |||
245 | uint32_t h_start_width; | ||
246 | uint32_t v_start_width; | ||
247 | enum disp_data_mapping disp_data_fmt; | ||
248 | }; | ||
249 | |||
250 | struct dma_chan_request { | ||
251 | struct mx3fb_data *mx3fb; | ||
252 | enum ipu_channel id; | ||
253 | }; | ||
254 | |||
255 | /* MX3 specific framebuffer information. */ | ||
256 | struct mx3fb_info { | ||
257 | int blank; | ||
258 | enum ipu_channel ipu_ch; | ||
259 | uint32_t cur_ipu_buf; | ||
260 | |||
261 | u32 pseudo_palette[16]; | ||
262 | |||
263 | struct completion flip_cmpl; | ||
264 | struct mutex mutex; /* Protects fb-ops */ | ||
265 | struct mx3fb_data *mx3fb; | ||
266 | struct idmac_channel *idmac_channel; | ||
267 | struct dma_async_tx_descriptor *txd; | ||
268 | dma_cookie_t cookie; | ||
269 | struct scatterlist sg[2]; | ||
270 | |||
271 | struct fb_var_screeninfo cur_var; /* current var info */ | ||
272 | }; | ||
273 | |||
274 | static void mx3fb_dma_done(void *); | ||
275 | |||
276 | /* Used fb-mode and bpp. Can be set on kernel command line, therefore file-static. */ | ||
277 | static const char *fb_mode; | ||
278 | static unsigned long default_bpp = 16; | ||
279 | |||
280 | static u32 mx3fb_read_reg(struct mx3fb_data *mx3fb, unsigned long reg) | ||
281 | { | ||
282 | return __raw_readl(mx3fb->reg_base + reg); | ||
283 | } | ||
284 | |||
285 | static void mx3fb_write_reg(struct mx3fb_data *mx3fb, u32 value, unsigned long reg) | ||
286 | { | ||
287 | __raw_writel(value, mx3fb->reg_base + reg); | ||
288 | } | ||
289 | |||
290 | struct di_mapping { | ||
291 | uint32_t b0, b1, b2; | ||
292 | }; | ||
293 | |||
294 | static const struct di_mapping di_mappings[] = { | ||
295 | [IPU_DISP_DATA_MAPPING_RGB666] = { 0x0005000f, 0x000b000f, 0x0011000f }, | ||
296 | [IPU_DISP_DATA_MAPPING_RGB565] = { 0x0004003f, 0x000a000f, 0x000f003f }, | ||
297 | [IPU_DISP_DATA_MAPPING_RGB888] = { 0x00070000, 0x000f0000, 0x00170000 }, | ||
298 | }; | ||
299 | |||
300 | static void sdc_fb_init(struct mx3fb_info *fbi) | ||
301 | { | ||
302 | struct mx3fb_data *mx3fb = fbi->mx3fb; | ||
303 | uint32_t reg; | ||
304 | |||
305 | reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); | ||
306 | |||
307 | mx3fb_write_reg(mx3fb, reg | SDC_COM_BG_EN, SDC_COM_CONF); | ||
308 | } | ||
309 | |||
310 | /* Returns enabled flag before uninit */ | ||
311 | static uint32_t sdc_fb_uninit(struct mx3fb_info *fbi) | ||
312 | { | ||
313 | struct mx3fb_data *mx3fb = fbi->mx3fb; | ||
314 | uint32_t reg; | ||
315 | |||
316 | reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); | ||
317 | |||
318 | mx3fb_write_reg(mx3fb, reg & ~SDC_COM_BG_EN, SDC_COM_CONF); | ||
319 | |||
320 | return reg & SDC_COM_BG_EN; | ||
321 | } | ||
322 | |||
323 | static void sdc_enable_channel(struct mx3fb_info *mx3_fbi) | ||
324 | { | ||
325 | struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; | ||
326 | struct idmac_channel *ichan = mx3_fbi->idmac_channel; | ||
327 | struct dma_chan *dma_chan = &ichan->dma_chan; | ||
328 | unsigned long flags; | ||
329 | dma_cookie_t cookie; | ||
330 | |||
331 | if (mx3_fbi->txd) | ||
332 | dev_dbg(mx3fb->dev, "mx3fbi %p, desc %p, sg %p\n", mx3_fbi, | ||
333 | to_tx_desc(mx3_fbi->txd), to_tx_desc(mx3_fbi->txd)->sg); | ||
334 | else | ||
335 | dev_dbg(mx3fb->dev, "mx3fbi %p, txd = NULL\n", mx3_fbi); | ||
336 | |||
337 | /* This enables the channel */ | ||
338 | if (mx3_fbi->cookie < 0) { | ||
339 | mx3_fbi->txd = dmaengine_prep_slave_sg(dma_chan, | ||
340 | &mx3_fbi->sg[0], 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); | ||
341 | if (!mx3_fbi->txd) { | ||
342 | dev_err(mx3fb->dev, "Cannot allocate descriptor on %d\n", | ||
343 | dma_chan->chan_id); | ||
344 | return; | ||
345 | } | ||
346 | |||
347 | mx3_fbi->txd->callback_param = mx3_fbi->txd; | ||
348 | mx3_fbi->txd->callback = mx3fb_dma_done; | ||
349 | |||
350 | cookie = mx3_fbi->txd->tx_submit(mx3_fbi->txd); | ||
351 | dev_dbg(mx3fb->dev, "%d: Submit %p #%d [%c]\n", __LINE__, | ||
352 | mx3_fbi->txd, cookie, list_empty(&ichan->queue) ? '-' : '+'); | ||
353 | } else { | ||
354 | if (!mx3_fbi->txd || !mx3_fbi->txd->tx_submit) { | ||
355 | dev_err(mx3fb->dev, "Cannot enable channel %d\n", | ||
356 | dma_chan->chan_id); | ||
357 | return; | ||
358 | } | ||
359 | |||
360 | /* Just re-activate the same buffer */ | ||
361 | dma_async_issue_pending(dma_chan); | ||
362 | cookie = mx3_fbi->cookie; | ||
363 | dev_dbg(mx3fb->dev, "%d: Re-submit %p #%d [%c]\n", __LINE__, | ||
364 | mx3_fbi->txd, cookie, list_empty(&ichan->queue) ? '-' : '+'); | ||
365 | } | ||
366 | |||
367 | if (cookie >= 0) { | ||
368 | spin_lock_irqsave(&mx3fb->lock, flags); | ||
369 | sdc_fb_init(mx3_fbi); | ||
370 | mx3_fbi->cookie = cookie; | ||
371 | spin_unlock_irqrestore(&mx3fb->lock, flags); | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * Attention! Without this msleep the channel keeps generating | ||
376 | * interrupts. Next sdc_set_brightness() is going to be called | ||
377 | * from mx3fb_blank(). | ||
378 | */ | ||
379 | msleep(2); | ||
380 | } | ||
381 | |||
382 | static void sdc_disable_channel(struct mx3fb_info *mx3_fbi) | ||
383 | { | ||
384 | struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; | ||
385 | uint32_t enabled; | ||
386 | unsigned long flags; | ||
387 | |||
388 | if (mx3_fbi->txd == NULL) | ||
389 | return; | ||
390 | |||
391 | spin_lock_irqsave(&mx3fb->lock, flags); | ||
392 | |||
393 | enabled = sdc_fb_uninit(mx3_fbi); | ||
394 | |||
395 | spin_unlock_irqrestore(&mx3fb->lock, flags); | ||
396 | |||
397 | mx3_fbi->txd->chan->device->device_control(mx3_fbi->txd->chan, | ||
398 | DMA_TERMINATE_ALL, 0); | ||
399 | mx3_fbi->txd = NULL; | ||
400 | mx3_fbi->cookie = -EINVAL; | ||
401 | } | ||
402 | |||
403 | /** | ||
404 | * sdc_set_window_pos() - set window position of the respective plane. | ||
405 | * @mx3fb: mx3fb context. | ||
406 | * @channel: IPU DMAC channel ID. | ||
407 | * @x_pos: X coordinate relative to the top left corner to place window at. | ||
408 | * @y_pos: Y coordinate relative to the top left corner to place window at. | ||
409 | * @return: 0 on success or negative error code on failure. | ||
410 | */ | ||
411 | static int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel, | ||
412 | int16_t x_pos, int16_t y_pos) | ||
413 | { | ||
414 | if (channel != IDMAC_SDC_0) | ||
415 | return -EINVAL; | ||
416 | |||
417 | x_pos += mx3fb->h_start_width; | ||
418 | y_pos += mx3fb->v_start_width; | ||
419 | |||
420 | mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS); | ||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | /** | ||
425 | * sdc_init_panel() - initialize a synchronous LCD panel. | ||
426 | * @mx3fb: mx3fb context. | ||
427 | * @panel: panel type. | ||
428 | * @pixel_clk: desired pixel clock frequency in Hz. | ||
429 | * @width: width of panel in pixels. | ||
430 | * @height: height of panel in pixels. | ||
431 | * @h_start_width: number of pixel clocks between the HSYNC signal pulse | ||
432 | * and the start of valid data. | ||
433 | * @h_sync_width: width of the HSYNC signal in units of pixel clocks. | ||
434 | * @h_end_width: number of pixel clocks between the end of valid data | ||
435 | * and the HSYNC signal for next line. | ||
436 | * @v_start_width: number of lines between the VSYNC signal pulse and the | ||
437 | * start of valid data. | ||
438 | * @v_sync_width: width of the VSYNC signal in units of lines | ||
439 | * @v_end_width: number of lines between the end of valid data and the | ||
440 | * VSYNC signal for next frame. | ||
441 | * @sig: bitfield of signal polarities for LCD interface. | ||
442 | * @return: 0 on success or negative error code on failure. | ||
443 | */ | ||
444 | static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel, | ||
445 | uint32_t pixel_clk, | ||
446 | uint16_t width, uint16_t height, | ||
447 | uint16_t h_start_width, uint16_t h_sync_width, | ||
448 | uint16_t h_end_width, uint16_t v_start_width, | ||
449 | uint16_t v_sync_width, uint16_t v_end_width, | ||
450 | struct ipu_di_signal_cfg sig) | ||
451 | { | ||
452 | unsigned long lock_flags; | ||
453 | uint32_t reg; | ||
454 | uint32_t old_conf; | ||
455 | uint32_t div; | ||
456 | struct clk *ipu_clk; | ||
457 | const struct di_mapping *map; | ||
458 | |||
459 | dev_dbg(mx3fb->dev, "panel size = %d x %d", width, height); | ||
460 | |||
461 | if (v_sync_width == 0 || h_sync_width == 0) | ||
462 | return -EINVAL; | ||
463 | |||
464 | /* Init panel size and blanking periods */ | ||
465 | reg = ((uint32_t) (h_sync_width - 1) << 26) | | ||
466 | ((uint32_t) (width + h_start_width + h_end_width - 1) << 16); | ||
467 | mx3fb_write_reg(mx3fb, reg, SDC_HOR_CONF); | ||
468 | |||
469 | #ifdef DEBUG | ||
470 | printk(KERN_CONT " hor_conf %x,", reg); | ||
471 | #endif | ||
472 | |||
473 | reg = ((uint32_t) (v_sync_width - 1) << 26) | SDC_V_SYNC_WIDTH_L | | ||
474 | ((uint32_t) (height + v_start_width + v_end_width - 1) << 16); | ||
475 | mx3fb_write_reg(mx3fb, reg, SDC_VER_CONF); | ||
476 | |||
477 | #ifdef DEBUG | ||
478 | printk(KERN_CONT " ver_conf %x\n", reg); | ||
479 | #endif | ||
480 | |||
481 | mx3fb->h_start_width = h_start_width; | ||
482 | mx3fb->v_start_width = v_start_width; | ||
483 | |||
484 | switch (panel) { | ||
485 | case IPU_PANEL_SHARP_TFT: | ||
486 | mx3fb_write_reg(mx3fb, 0x00FD0102L, SDC_SHARP_CONF_1); | ||
487 | mx3fb_write_reg(mx3fb, 0x00F500F4L, SDC_SHARP_CONF_2); | ||
488 | mx3fb_write_reg(mx3fb, SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF); | ||
489 | break; | ||
490 | case IPU_PANEL_TFT: | ||
491 | mx3fb_write_reg(mx3fb, SDC_COM_TFT_COLOR, SDC_COM_CONF); | ||
492 | break; | ||
493 | default: | ||
494 | return -EINVAL; | ||
495 | } | ||
496 | |||
497 | /* Init clocking */ | ||
498 | |||
499 | /* | ||
500 | * Calculate divider: fractional part is 4 bits so simply multiple by | ||
501 | * 2^4 to get fractional part, as long as we stay under ~250MHz and on | ||
502 | * i.MX31 it (HSP_CLK) is <= 178MHz. Currently 128.267MHz | ||
503 | */ | ||
504 | ipu_clk = clk_get(mx3fb->dev, NULL); | ||
505 | if (!IS_ERR(ipu_clk)) { | ||
506 | div = clk_get_rate(ipu_clk) * 16 / pixel_clk; | ||
507 | clk_put(ipu_clk); | ||
508 | } else { | ||
509 | div = 0; | ||
510 | } | ||
511 | |||
512 | if (div < 0x40) { /* Divider less than 4 */ | ||
513 | dev_dbg(mx3fb->dev, | ||
514 | "InitPanel() - Pixel clock divider less than 4\n"); | ||
515 | div = 0x40; | ||
516 | } | ||
517 | |||
518 | dev_dbg(mx3fb->dev, "pixel clk = %u, divider %u.%u\n", | ||
519 | pixel_clk, div >> 4, (div & 7) * 125); | ||
520 | |||
521 | spin_lock_irqsave(&mx3fb->lock, lock_flags); | ||
522 | |||
523 | /* | ||
524 | * DISP3_IF_CLK_DOWN_WR is half the divider value and 2 fraction bits | ||
525 | * fewer. Subtract 1 extra from DISP3_IF_CLK_DOWN_WR based on timing | ||
526 | * debug. DISP3_IF_CLK_UP_WR is 0 | ||
527 | */ | ||
528 | mx3fb_write_reg(mx3fb, (((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF); | ||
529 | |||
530 | /* DI settings */ | ||
531 | old_conf = mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF) & 0x78FFFFFF; | ||
532 | old_conf |= sig.datamask_en << DI_D3_DATAMSK_SHIFT | | ||
533 | sig.clksel_en << DI_D3_CLK_SEL_SHIFT | | ||
534 | sig.clkidle_en << DI_D3_CLK_IDLE_SHIFT; | ||
535 | mx3fb_write_reg(mx3fb, old_conf, DI_DISP_IF_CONF); | ||
536 | |||
537 | old_conf = mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL) & 0xE0FFFFFF; | ||
538 | old_conf |= sig.data_pol << DI_D3_DATA_POL_SHIFT | | ||
539 | sig.clk_pol << DI_D3_CLK_POL_SHIFT | | ||
540 | sig.enable_pol << DI_D3_DRDY_SHARP_POL_SHIFT | | ||
541 | sig.Hsync_pol << DI_D3_HSYNC_POL_SHIFT | | ||
542 | sig.Vsync_pol << DI_D3_VSYNC_POL_SHIFT; | ||
543 | mx3fb_write_reg(mx3fb, old_conf, DI_DISP_SIG_POL); | ||
544 | |||
545 | map = &di_mappings[mx3fb->disp_data_fmt]; | ||
546 | mx3fb_write_reg(mx3fb, map->b0, DI_DISP3_B0_MAP); | ||
547 | mx3fb_write_reg(mx3fb, map->b1, DI_DISP3_B1_MAP); | ||
548 | mx3fb_write_reg(mx3fb, map->b2, DI_DISP3_B2_MAP); | ||
549 | |||
550 | spin_unlock_irqrestore(&mx3fb->lock, lock_flags); | ||
551 | |||
552 | dev_dbg(mx3fb->dev, "DI_DISP_IF_CONF = 0x%08X\n", | ||
553 | mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF)); | ||
554 | dev_dbg(mx3fb->dev, "DI_DISP_SIG_POL = 0x%08X\n", | ||
555 | mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL)); | ||
556 | dev_dbg(mx3fb->dev, "DI_DISP3_TIME_CONF = 0x%08X\n", | ||
557 | mx3fb_read_reg(mx3fb, DI_DISP3_TIME_CONF)); | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | /** | ||
563 | * sdc_set_color_key() - set the transparent color key for SDC graphic plane. | ||
564 | * @mx3fb: mx3fb context. | ||
565 | * @channel: IPU DMAC channel ID. | ||
566 | * @enable: boolean to enable or disable color keyl. | ||
567 | * @color_key: 24-bit RGB color to use as transparent color key. | ||
568 | * @return: 0 on success or negative error code on failure. | ||
569 | */ | ||
570 | static int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel, | ||
571 | bool enable, uint32_t color_key) | ||
572 | { | ||
573 | uint32_t reg, sdc_conf; | ||
574 | unsigned long lock_flags; | ||
575 | |||
576 | spin_lock_irqsave(&mx3fb->lock, lock_flags); | ||
577 | |||
578 | sdc_conf = mx3fb_read_reg(mx3fb, SDC_COM_CONF); | ||
579 | if (channel == IDMAC_SDC_0) | ||
580 | sdc_conf &= ~SDC_COM_GWSEL; | ||
581 | else | ||
582 | sdc_conf |= SDC_COM_GWSEL; | ||
583 | |||
584 | if (enable) { | ||
585 | reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0xFF000000L; | ||
586 | mx3fb_write_reg(mx3fb, reg | (color_key & 0x00FFFFFFL), | ||
587 | SDC_GW_CTRL); | ||
588 | |||
589 | sdc_conf |= SDC_COM_KEY_COLOR_G; | ||
590 | } else { | ||
591 | sdc_conf &= ~SDC_COM_KEY_COLOR_G; | ||
592 | } | ||
593 | mx3fb_write_reg(mx3fb, sdc_conf, SDC_COM_CONF); | ||
594 | |||
595 | spin_unlock_irqrestore(&mx3fb->lock, lock_flags); | ||
596 | |||
597 | return 0; | ||
598 | } | ||
599 | |||
600 | /** | ||
601 | * sdc_set_global_alpha() - set global alpha blending modes. | ||
602 | * @mx3fb: mx3fb context. | ||
603 | * @enable: boolean to enable or disable global alpha blending. If disabled, | ||
604 | * per pixel blending is used. | ||
605 | * @alpha: global alpha value. | ||
606 | * @return: 0 on success or negative error code on failure. | ||
607 | */ | ||
608 | static int sdc_set_global_alpha(struct mx3fb_data *mx3fb, bool enable, uint8_t alpha) | ||
609 | { | ||
610 | uint32_t reg; | ||
611 | unsigned long lock_flags; | ||
612 | |||
613 | spin_lock_irqsave(&mx3fb->lock, lock_flags); | ||
614 | |||
615 | if (enable) { | ||
616 | reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0x00FFFFFFL; | ||
617 | mx3fb_write_reg(mx3fb, reg | ((uint32_t) alpha << 24), SDC_GW_CTRL); | ||
618 | |||
619 | reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); | ||
620 | mx3fb_write_reg(mx3fb, reg | SDC_COM_GLB_A, SDC_COM_CONF); | ||
621 | } else { | ||
622 | reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); | ||
623 | mx3fb_write_reg(mx3fb, reg & ~SDC_COM_GLB_A, SDC_COM_CONF); | ||
624 | } | ||
625 | |||
626 | spin_unlock_irqrestore(&mx3fb->lock, lock_flags); | ||
627 | |||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | static void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value) | ||
632 | { | ||
633 | dev_dbg(mx3fb->dev, "%s: value = %d\n", __func__, value); | ||
634 | /* This might be board-specific */ | ||
635 | mx3fb_write_reg(mx3fb, 0x03000000UL | value << 16, SDC_PWM_CTRL); | ||
636 | return; | ||
637 | } | ||
638 | |||
639 | static uint32_t bpp_to_pixfmt(int bpp) | ||
640 | { | ||
641 | uint32_t pixfmt = 0; | ||
642 | switch (bpp) { | ||
643 | case 24: | ||
644 | pixfmt = IPU_PIX_FMT_BGR24; | ||
645 | break; | ||
646 | case 32: | ||
647 | pixfmt = IPU_PIX_FMT_BGR32; | ||
648 | break; | ||
649 | case 16: | ||
650 | pixfmt = IPU_PIX_FMT_RGB565; | ||
651 | break; | ||
652 | } | ||
653 | return pixfmt; | ||
654 | } | ||
655 | |||
656 | static int mx3fb_blank(int blank, struct fb_info *fbi); | ||
657 | static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len, | ||
658 | bool lock); | ||
659 | static int mx3fb_unmap_video_memory(struct fb_info *fbi); | ||
660 | |||
661 | /** | ||
662 | * mx3fb_set_fix() - set fixed framebuffer parameters from variable settings. | ||
663 | * @info: framebuffer information pointer | ||
664 | * @return: 0 on success or negative error code on failure. | ||
665 | */ | ||
666 | static int mx3fb_set_fix(struct fb_info *fbi) | ||
667 | { | ||
668 | struct fb_fix_screeninfo *fix = &fbi->fix; | ||
669 | struct fb_var_screeninfo *var = &fbi->var; | ||
670 | |||
671 | strncpy(fix->id, "DISP3 BG", 8); | ||
672 | |||
673 | fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; | ||
674 | |||
675 | fix->type = FB_TYPE_PACKED_PIXELS; | ||
676 | fix->accel = FB_ACCEL_NONE; | ||
677 | fix->visual = FB_VISUAL_TRUECOLOR; | ||
678 | fix->xpanstep = 1; | ||
679 | fix->ypanstep = 1; | ||
680 | |||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | static void mx3fb_dma_done(void *arg) | ||
685 | { | ||
686 | struct idmac_tx_desc *tx_desc = to_tx_desc(arg); | ||
687 | struct dma_chan *chan = tx_desc->txd.chan; | ||
688 | struct idmac_channel *ichannel = to_idmac_chan(chan); | ||
689 | struct mx3fb_data *mx3fb = ichannel->client; | ||
690 | struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; | ||
691 | |||
692 | dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq); | ||
693 | |||
694 | /* We only need one interrupt, it will be re-enabled as needed */ | ||
695 | disable_irq_nosync(ichannel->eof_irq); | ||
696 | |||
697 | complete(&mx3_fbi->flip_cmpl); | ||
698 | } | ||
699 | |||
700 | static bool mx3fb_must_set_par(struct fb_info *fbi) | ||
701 | { | ||
702 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
703 | struct fb_var_screeninfo old_var = mx3_fbi->cur_var; | ||
704 | struct fb_var_screeninfo new_var = fbi->var; | ||
705 | |||
706 | if ((fbi->var.activate & FB_ACTIVATE_FORCE) && | ||
707 | (fbi->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) | ||
708 | return true; | ||
709 | |||
710 | /* | ||
711 | * Ignore xoffset and yoffset update, | ||
712 | * because pan display handles this case. | ||
713 | */ | ||
714 | old_var.xoffset = new_var.xoffset; | ||
715 | old_var.yoffset = new_var.yoffset; | ||
716 | |||
717 | return !!memcmp(&old_var, &new_var, sizeof(struct fb_var_screeninfo)); | ||
718 | } | ||
719 | |||
720 | static int __set_par(struct fb_info *fbi, bool lock) | ||
721 | { | ||
722 | u32 mem_len, cur_xoffset, cur_yoffset; | ||
723 | struct ipu_di_signal_cfg sig_cfg; | ||
724 | enum ipu_panel mode = IPU_PANEL_TFT; | ||
725 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
726 | struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; | ||
727 | struct idmac_channel *ichan = mx3_fbi->idmac_channel; | ||
728 | struct idmac_video_param *video = &ichan->params.video; | ||
729 | struct scatterlist *sg = mx3_fbi->sg; | ||
730 | |||
731 | /* Total cleanup */ | ||
732 | if (mx3_fbi->txd) | ||
733 | sdc_disable_channel(mx3_fbi); | ||
734 | |||
735 | mx3fb_set_fix(fbi); | ||
736 | |||
737 | mem_len = fbi->var.yres_virtual * fbi->fix.line_length; | ||
738 | if (mem_len > fbi->fix.smem_len) { | ||
739 | if (fbi->fix.smem_start) | ||
740 | mx3fb_unmap_video_memory(fbi); | ||
741 | |||
742 | if (mx3fb_map_video_memory(fbi, mem_len, lock) < 0) | ||
743 | return -ENOMEM; | ||
744 | } | ||
745 | |||
746 | sg_init_table(&sg[0], 1); | ||
747 | sg_init_table(&sg[1], 1); | ||
748 | |||
749 | sg_dma_address(&sg[0]) = fbi->fix.smem_start; | ||
750 | sg_set_page(&sg[0], virt_to_page(fbi->screen_base), | ||
751 | fbi->fix.smem_len, | ||
752 | offset_in_page(fbi->screen_base)); | ||
753 | |||
754 | if (mx3_fbi->ipu_ch == IDMAC_SDC_0) { | ||
755 | memset(&sig_cfg, 0, sizeof(sig_cfg)); | ||
756 | if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) | ||
757 | sig_cfg.Hsync_pol = true; | ||
758 | if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) | ||
759 | sig_cfg.Vsync_pol = true; | ||
760 | if (fbi->var.sync & FB_SYNC_CLK_INVERT) | ||
761 | sig_cfg.clk_pol = true; | ||
762 | if (fbi->var.sync & FB_SYNC_DATA_INVERT) | ||
763 | sig_cfg.data_pol = true; | ||
764 | if (fbi->var.sync & FB_SYNC_OE_ACT_HIGH) | ||
765 | sig_cfg.enable_pol = true; | ||
766 | if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) | ||
767 | sig_cfg.clkidle_en = true; | ||
768 | if (fbi->var.sync & FB_SYNC_CLK_SEL_EN) | ||
769 | sig_cfg.clksel_en = true; | ||
770 | if (fbi->var.sync & FB_SYNC_SHARP_MODE) | ||
771 | mode = IPU_PANEL_SHARP_TFT; | ||
772 | |||
773 | dev_dbg(fbi->device, "pixclock = %ul Hz\n", | ||
774 | (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); | ||
775 | |||
776 | if (sdc_init_panel(mx3fb, mode, | ||
777 | (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, | ||
778 | fbi->var.xres, fbi->var.yres, | ||
779 | fbi->var.left_margin, | ||
780 | fbi->var.hsync_len, | ||
781 | fbi->var.right_margin + | ||
782 | fbi->var.hsync_len, | ||
783 | fbi->var.upper_margin, | ||
784 | fbi->var.vsync_len, | ||
785 | fbi->var.lower_margin + | ||
786 | fbi->var.vsync_len, sig_cfg) != 0) { | ||
787 | dev_err(fbi->device, | ||
788 | "mx3fb: Error initializing panel.\n"); | ||
789 | return -EINVAL; | ||
790 | } | ||
791 | } | ||
792 | |||
793 | sdc_set_window_pos(mx3fb, mx3_fbi->ipu_ch, 0, 0); | ||
794 | |||
795 | mx3_fbi->cur_ipu_buf = 0; | ||
796 | |||
797 | video->out_pixel_fmt = bpp_to_pixfmt(fbi->var.bits_per_pixel); | ||
798 | video->out_width = fbi->var.xres; | ||
799 | video->out_height = fbi->var.yres; | ||
800 | video->out_stride = fbi->var.xres_virtual; | ||
801 | |||
802 | if (mx3_fbi->blank == FB_BLANK_UNBLANK) { | ||
803 | sdc_enable_channel(mx3_fbi); | ||
804 | /* | ||
805 | * sg[0] points to fb smem_start address | ||
806 | * and is actually active in controller. | ||
807 | */ | ||
808 | mx3_fbi->cur_var.xoffset = 0; | ||
809 | mx3_fbi->cur_var.yoffset = 0; | ||
810 | } | ||
811 | |||
812 | /* | ||
813 | * Preserve xoffset and yoffest in case they are | ||
814 | * inactive in controller as fb is blanked. | ||
815 | */ | ||
816 | cur_xoffset = mx3_fbi->cur_var.xoffset; | ||
817 | cur_yoffset = mx3_fbi->cur_var.yoffset; | ||
818 | mx3_fbi->cur_var = fbi->var; | ||
819 | mx3_fbi->cur_var.xoffset = cur_xoffset; | ||
820 | mx3_fbi->cur_var.yoffset = cur_yoffset; | ||
821 | |||
822 | return 0; | ||
823 | } | ||
824 | |||
825 | /** | ||
826 | * mx3fb_set_par() - set framebuffer parameters and change the operating mode. | ||
827 | * @fbi: framebuffer information pointer. | ||
828 | * @return: 0 on success or negative error code on failure. | ||
829 | */ | ||
830 | static int mx3fb_set_par(struct fb_info *fbi) | ||
831 | { | ||
832 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
833 | struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; | ||
834 | struct idmac_channel *ichan = mx3_fbi->idmac_channel; | ||
835 | int ret; | ||
836 | |||
837 | dev_dbg(mx3fb->dev, "%s [%c]\n", __func__, list_empty(&ichan->queue) ? '-' : '+'); | ||
838 | |||
839 | mutex_lock(&mx3_fbi->mutex); | ||
840 | |||
841 | ret = mx3fb_must_set_par(fbi) ? __set_par(fbi, true) : 0; | ||
842 | |||
843 | mutex_unlock(&mx3_fbi->mutex); | ||
844 | |||
845 | return ret; | ||
846 | } | ||
847 | |||
848 | /** | ||
849 | * mx3fb_check_var() - check and adjust framebuffer variable parameters. | ||
850 | * @var: framebuffer variable parameters | ||
851 | * @fbi: framebuffer information pointer | ||
852 | */ | ||
853 | static int mx3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) | ||
854 | { | ||
855 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
856 | u32 vtotal; | ||
857 | u32 htotal; | ||
858 | |||
859 | dev_dbg(fbi->device, "%s\n", __func__); | ||
860 | |||
861 | if (var->xres_virtual < var->xres) | ||
862 | var->xres_virtual = var->xres; | ||
863 | if (var->yres_virtual < var->yres) | ||
864 | var->yres_virtual = var->yres; | ||
865 | |||
866 | if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && | ||
867 | (var->bits_per_pixel != 16)) | ||
868 | var->bits_per_pixel = default_bpp; | ||
869 | |||
870 | switch (var->bits_per_pixel) { | ||
871 | case 16: | ||
872 | var->red.length = 5; | ||
873 | var->red.offset = 11; | ||
874 | var->red.msb_right = 0; | ||
875 | |||
876 | var->green.length = 6; | ||
877 | var->green.offset = 5; | ||
878 | var->green.msb_right = 0; | ||
879 | |||
880 | var->blue.length = 5; | ||
881 | var->blue.offset = 0; | ||
882 | var->blue.msb_right = 0; | ||
883 | |||
884 | var->transp.length = 0; | ||
885 | var->transp.offset = 0; | ||
886 | var->transp.msb_right = 0; | ||
887 | break; | ||
888 | case 24: | ||
889 | var->red.length = 8; | ||
890 | var->red.offset = 16; | ||
891 | var->red.msb_right = 0; | ||
892 | |||
893 | var->green.length = 8; | ||
894 | var->green.offset = 8; | ||
895 | var->green.msb_right = 0; | ||
896 | |||
897 | var->blue.length = 8; | ||
898 | var->blue.offset = 0; | ||
899 | var->blue.msb_right = 0; | ||
900 | |||
901 | var->transp.length = 0; | ||
902 | var->transp.offset = 0; | ||
903 | var->transp.msb_right = 0; | ||
904 | break; | ||
905 | case 32: | ||
906 | var->red.length = 8; | ||
907 | var->red.offset = 16; | ||
908 | var->red.msb_right = 0; | ||
909 | |||
910 | var->green.length = 8; | ||
911 | var->green.offset = 8; | ||
912 | var->green.msb_right = 0; | ||
913 | |||
914 | var->blue.length = 8; | ||
915 | var->blue.offset = 0; | ||
916 | var->blue.msb_right = 0; | ||
917 | |||
918 | var->transp.length = 8; | ||
919 | var->transp.offset = 24; | ||
920 | var->transp.msb_right = 0; | ||
921 | break; | ||
922 | } | ||
923 | |||
924 | if (var->pixclock < 1000) { | ||
925 | htotal = var->xres + var->right_margin + var->hsync_len + | ||
926 | var->left_margin; | ||
927 | vtotal = var->yres + var->lower_margin + var->vsync_len + | ||
928 | var->upper_margin; | ||
929 | var->pixclock = (vtotal * htotal * 6UL) / 100UL; | ||
930 | var->pixclock = KHZ2PICOS(var->pixclock); | ||
931 | dev_dbg(fbi->device, "pixclock set for 60Hz refresh = %u ps\n", | ||
932 | var->pixclock); | ||
933 | } | ||
934 | |||
935 | var->height = -1; | ||
936 | var->width = -1; | ||
937 | var->grayscale = 0; | ||
938 | |||
939 | /* Preserve sync flags */ | ||
940 | var->sync |= mx3_fbi->cur_var.sync; | ||
941 | mx3_fbi->cur_var.sync |= var->sync; | ||
942 | |||
943 | return 0; | ||
944 | } | ||
945 | |||
946 | static u32 chan_to_field(unsigned int chan, struct fb_bitfield *bf) | ||
947 | { | ||
948 | chan &= 0xffff; | ||
949 | chan >>= 16 - bf->length; | ||
950 | return chan << bf->offset; | ||
951 | } | ||
952 | |||
953 | static int mx3fb_setcolreg(unsigned int regno, unsigned int red, | ||
954 | unsigned int green, unsigned int blue, | ||
955 | unsigned int trans, struct fb_info *fbi) | ||
956 | { | ||
957 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
958 | u32 val; | ||
959 | int ret = 1; | ||
960 | |||
961 | dev_dbg(fbi->device, "%s, regno = %u\n", __func__, regno); | ||
962 | |||
963 | mutex_lock(&mx3_fbi->mutex); | ||
964 | /* | ||
965 | * If greyscale is true, then we convert the RGB value | ||
966 | * to greyscale no matter what visual we are using. | ||
967 | */ | ||
968 | if (fbi->var.grayscale) | ||
969 | red = green = blue = (19595 * red + 38470 * green + | ||
970 | 7471 * blue) >> 16; | ||
971 | switch (fbi->fix.visual) { | ||
972 | case FB_VISUAL_TRUECOLOR: | ||
973 | /* | ||
974 | * 16-bit True Colour. We encode the RGB value | ||
975 | * according to the RGB bitfield information. | ||
976 | */ | ||
977 | if (regno < 16) { | ||
978 | u32 *pal = fbi->pseudo_palette; | ||
979 | |||
980 | val = chan_to_field(red, &fbi->var.red); | ||
981 | val |= chan_to_field(green, &fbi->var.green); | ||
982 | val |= chan_to_field(blue, &fbi->var.blue); | ||
983 | |||
984 | pal[regno] = val; | ||
985 | |||
986 | ret = 0; | ||
987 | } | ||
988 | break; | ||
989 | |||
990 | case FB_VISUAL_STATIC_PSEUDOCOLOR: | ||
991 | case FB_VISUAL_PSEUDOCOLOR: | ||
992 | break; | ||
993 | } | ||
994 | mutex_unlock(&mx3_fbi->mutex); | ||
995 | |||
996 | return ret; | ||
997 | } | ||
998 | |||
999 | static void __blank(int blank, struct fb_info *fbi) | ||
1000 | { | ||
1001 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
1002 | struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; | ||
1003 | int was_blank = mx3_fbi->blank; | ||
1004 | |||
1005 | mx3_fbi->blank = blank; | ||
1006 | |||
1007 | /* Attention! | ||
1008 | * Do not call sdc_disable_channel() for a channel that is disabled | ||
1009 | * already! This will result in a kernel NULL pointer dereference | ||
1010 | * (mx3_fbi->txd is NULL). Hide the fact, that all blank modes are | ||
1011 | * handled equally by this driver. | ||
1012 | */ | ||
1013 | if (blank > FB_BLANK_UNBLANK && was_blank > FB_BLANK_UNBLANK) | ||
1014 | return; | ||
1015 | |||
1016 | switch (blank) { | ||
1017 | case FB_BLANK_POWERDOWN: | ||
1018 | case FB_BLANK_VSYNC_SUSPEND: | ||
1019 | case FB_BLANK_HSYNC_SUSPEND: | ||
1020 | case FB_BLANK_NORMAL: | ||
1021 | sdc_set_brightness(mx3fb, 0); | ||
1022 | memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); | ||
1023 | /* Give LCD time to update - enough for 50 and 60 Hz */ | ||
1024 | msleep(25); | ||
1025 | sdc_disable_channel(mx3_fbi); | ||
1026 | break; | ||
1027 | case FB_BLANK_UNBLANK: | ||
1028 | sdc_enable_channel(mx3_fbi); | ||
1029 | sdc_set_brightness(mx3fb, mx3fb->backlight_level); | ||
1030 | break; | ||
1031 | } | ||
1032 | } | ||
1033 | |||
1034 | /** | ||
1035 | * mx3fb_blank() - blank the display. | ||
1036 | */ | ||
1037 | static int mx3fb_blank(int blank, struct fb_info *fbi) | ||
1038 | { | ||
1039 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
1040 | |||
1041 | dev_dbg(fbi->device, "%s, blank = %d, base %p, len %u\n", __func__, | ||
1042 | blank, fbi->screen_base, fbi->fix.smem_len); | ||
1043 | |||
1044 | if (mx3_fbi->blank == blank) | ||
1045 | return 0; | ||
1046 | |||
1047 | mutex_lock(&mx3_fbi->mutex); | ||
1048 | __blank(blank, fbi); | ||
1049 | mutex_unlock(&mx3_fbi->mutex); | ||
1050 | |||
1051 | return 0; | ||
1052 | } | ||
1053 | |||
1054 | /** | ||
1055 | * mx3fb_pan_display() - pan or wrap the display | ||
1056 | * @var: variable screen buffer information. | ||
1057 | * @info: framebuffer information pointer. | ||
1058 | * | ||
1059 | * We look only at xoffset, yoffset and the FB_VMODE_YWRAP flag | ||
1060 | */ | ||
1061 | static int mx3fb_pan_display(struct fb_var_screeninfo *var, | ||
1062 | struct fb_info *fbi) | ||
1063 | { | ||
1064 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
1065 | u32 y_bottom; | ||
1066 | unsigned long base; | ||
1067 | off_t offset; | ||
1068 | dma_cookie_t cookie; | ||
1069 | struct scatterlist *sg = mx3_fbi->sg; | ||
1070 | struct dma_chan *dma_chan = &mx3_fbi->idmac_channel->dma_chan; | ||
1071 | struct dma_async_tx_descriptor *txd; | ||
1072 | int ret; | ||
1073 | |||
1074 | dev_dbg(fbi->device, "%s [%c]\n", __func__, | ||
1075 | list_empty(&mx3_fbi->idmac_channel->queue) ? '-' : '+'); | ||
1076 | |||
1077 | if (var->xoffset > 0) { | ||
1078 | dev_dbg(fbi->device, "x panning not supported\n"); | ||
1079 | return -EINVAL; | ||
1080 | } | ||
1081 | |||
1082 | if (mx3_fbi->cur_var.xoffset == var->xoffset && | ||
1083 | mx3_fbi->cur_var.yoffset == var->yoffset) | ||
1084 | return 0; /* No change, do nothing */ | ||
1085 | |||
1086 | y_bottom = var->yoffset; | ||
1087 | |||
1088 | if (!(var->vmode & FB_VMODE_YWRAP)) | ||
1089 | y_bottom += fbi->var.yres; | ||
1090 | |||
1091 | if (y_bottom > fbi->var.yres_virtual) | ||
1092 | return -EINVAL; | ||
1093 | |||
1094 | mutex_lock(&mx3_fbi->mutex); | ||
1095 | |||
1096 | offset = var->yoffset * fbi->fix.line_length | ||
1097 | + var->xoffset * (fbi->var.bits_per_pixel / 8); | ||
1098 | base = fbi->fix.smem_start + offset; | ||
1099 | |||
1100 | dev_dbg(fbi->device, "Updating SDC BG buf %d address=0x%08lX\n", | ||
1101 | mx3_fbi->cur_ipu_buf, base); | ||
1102 | |||
1103 | /* | ||
1104 | * We enable the End of Frame interrupt, which will free a tx-descriptor, | ||
1105 | * which we will need for the next device_prep_slave_sg(). The | ||
1106 | * IRQ-handler will disable the IRQ again. | ||
1107 | */ | ||
1108 | init_completion(&mx3_fbi->flip_cmpl); | ||
1109 | enable_irq(mx3_fbi->idmac_channel->eof_irq); | ||
1110 | |||
1111 | ret = wait_for_completion_timeout(&mx3_fbi->flip_cmpl, HZ / 10); | ||
1112 | if (ret <= 0) { | ||
1113 | mutex_unlock(&mx3_fbi->mutex); | ||
1114 | dev_info(fbi->device, "Panning failed due to %s\n", ret < 0 ? | ||
1115 | "user interrupt" : "timeout"); | ||
1116 | disable_irq(mx3_fbi->idmac_channel->eof_irq); | ||
1117 | return ret ? : -ETIMEDOUT; | ||
1118 | } | ||
1119 | |||
1120 | mx3_fbi->cur_ipu_buf = !mx3_fbi->cur_ipu_buf; | ||
1121 | |||
1122 | sg_dma_address(&sg[mx3_fbi->cur_ipu_buf]) = base; | ||
1123 | sg_set_page(&sg[mx3_fbi->cur_ipu_buf], | ||
1124 | virt_to_page(fbi->screen_base + offset), fbi->fix.smem_len, | ||
1125 | offset_in_page(fbi->screen_base + offset)); | ||
1126 | |||
1127 | if (mx3_fbi->txd) | ||
1128 | async_tx_ack(mx3_fbi->txd); | ||
1129 | |||
1130 | txd = dmaengine_prep_slave_sg(dma_chan, sg + | ||
1131 | mx3_fbi->cur_ipu_buf, 1, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT); | ||
1132 | if (!txd) { | ||
1133 | dev_err(fbi->device, | ||
1134 | "Error preparing a DMA transaction descriptor.\n"); | ||
1135 | mutex_unlock(&mx3_fbi->mutex); | ||
1136 | return -EIO; | ||
1137 | } | ||
1138 | |||
1139 | txd->callback_param = txd; | ||
1140 | txd->callback = mx3fb_dma_done; | ||
1141 | |||
1142 | /* | ||
1143 | * Emulate original mx3fb behaviour: each new call to idmac_tx_submit() | ||
1144 | * should switch to another buffer | ||
1145 | */ | ||
1146 | cookie = txd->tx_submit(txd); | ||
1147 | dev_dbg(fbi->device, "%d: Submit %p #%d\n", __LINE__, txd, cookie); | ||
1148 | if (cookie < 0) { | ||
1149 | dev_err(fbi->device, | ||
1150 | "Error updating SDC buf %d to address=0x%08lX\n", | ||
1151 | mx3_fbi->cur_ipu_buf, base); | ||
1152 | mutex_unlock(&mx3_fbi->mutex); | ||
1153 | return -EIO; | ||
1154 | } | ||
1155 | |||
1156 | mx3_fbi->txd = txd; | ||
1157 | |||
1158 | fbi->var.xoffset = var->xoffset; | ||
1159 | fbi->var.yoffset = var->yoffset; | ||
1160 | |||
1161 | if (var->vmode & FB_VMODE_YWRAP) | ||
1162 | fbi->var.vmode |= FB_VMODE_YWRAP; | ||
1163 | else | ||
1164 | fbi->var.vmode &= ~FB_VMODE_YWRAP; | ||
1165 | |||
1166 | mx3_fbi->cur_var = fbi->var; | ||
1167 | |||
1168 | mutex_unlock(&mx3_fbi->mutex); | ||
1169 | |||
1170 | dev_dbg(fbi->device, "Update complete\n"); | ||
1171 | |||
1172 | return 0; | ||
1173 | } | ||
1174 | |||
1175 | /* | ||
1176 | * This structure contains the pointers to the control functions that are | ||
1177 | * invoked by the core framebuffer driver to perform operations like | ||
1178 | * blitting, rectangle filling, copy regions and cursor definition. | ||
1179 | */ | ||
1180 | static struct fb_ops mx3fb_ops = { | ||
1181 | .owner = THIS_MODULE, | ||
1182 | .fb_set_par = mx3fb_set_par, | ||
1183 | .fb_check_var = mx3fb_check_var, | ||
1184 | .fb_setcolreg = mx3fb_setcolreg, | ||
1185 | .fb_pan_display = mx3fb_pan_display, | ||
1186 | .fb_fillrect = cfb_fillrect, | ||
1187 | .fb_copyarea = cfb_copyarea, | ||
1188 | .fb_imageblit = cfb_imageblit, | ||
1189 | .fb_blank = mx3fb_blank, | ||
1190 | }; | ||
1191 | |||
1192 | #ifdef CONFIG_PM | ||
1193 | /* | ||
1194 | * Power management hooks. Note that we won't be called from IRQ context, | ||
1195 | * unlike the blank functions above, so we may sleep. | ||
1196 | */ | ||
1197 | |||
1198 | /* | ||
1199 | * Suspends the framebuffer and blanks the screen. Power management support | ||
1200 | */ | ||
1201 | static int mx3fb_suspend(struct platform_device *pdev, pm_message_t state) | ||
1202 | { | ||
1203 | struct mx3fb_data *mx3fb = platform_get_drvdata(pdev); | ||
1204 | struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; | ||
1205 | |||
1206 | console_lock(); | ||
1207 | fb_set_suspend(mx3fb->fbi, 1); | ||
1208 | console_unlock(); | ||
1209 | |||
1210 | if (mx3_fbi->blank == FB_BLANK_UNBLANK) { | ||
1211 | sdc_disable_channel(mx3_fbi); | ||
1212 | sdc_set_brightness(mx3fb, 0); | ||
1213 | |||
1214 | } | ||
1215 | return 0; | ||
1216 | } | ||
1217 | |||
1218 | /* | ||
1219 | * Resumes the framebuffer and unblanks the screen. Power management support | ||
1220 | */ | ||
1221 | static int mx3fb_resume(struct platform_device *pdev) | ||
1222 | { | ||
1223 | struct mx3fb_data *mx3fb = platform_get_drvdata(pdev); | ||
1224 | struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; | ||
1225 | |||
1226 | if (mx3_fbi->blank == FB_BLANK_UNBLANK) { | ||
1227 | sdc_enable_channel(mx3_fbi); | ||
1228 | sdc_set_brightness(mx3fb, mx3fb->backlight_level); | ||
1229 | } | ||
1230 | |||
1231 | console_lock(); | ||
1232 | fb_set_suspend(mx3fb->fbi, 0); | ||
1233 | console_unlock(); | ||
1234 | |||
1235 | return 0; | ||
1236 | } | ||
1237 | #else | ||
1238 | #define mx3fb_suspend NULL | ||
1239 | #define mx3fb_resume NULL | ||
1240 | #endif | ||
1241 | |||
1242 | /* | ||
1243 | * Main framebuffer functions | ||
1244 | */ | ||
1245 | |||
1246 | /** | ||
1247 | * mx3fb_map_video_memory() - allocates the DRAM memory for the frame buffer. | ||
1248 | * @fbi: framebuffer information pointer | ||
1249 | * @mem_len: length of mapped memory | ||
1250 | * @lock: do not lock during initialisation | ||
1251 | * @return: Error code indicating success or failure | ||
1252 | * | ||
1253 | * This buffer is remapped into a non-cached, non-buffered, memory region to | ||
1254 | * allow palette and pixel writes to occur without flushing the cache. Once this | ||
1255 | * area is remapped, all virtual memory access to the video memory should occur | ||
1256 | * at the new region. | ||
1257 | */ | ||
1258 | static int mx3fb_map_video_memory(struct fb_info *fbi, unsigned int mem_len, | ||
1259 | bool lock) | ||
1260 | { | ||
1261 | int retval = 0; | ||
1262 | dma_addr_t addr; | ||
1263 | |||
1264 | fbi->screen_base = dma_alloc_writecombine(fbi->device, | ||
1265 | mem_len, | ||
1266 | &addr, GFP_DMA | GFP_KERNEL); | ||
1267 | |||
1268 | if (!fbi->screen_base) { | ||
1269 | dev_err(fbi->device, "Cannot allocate %u bytes framebuffer memory\n", | ||
1270 | mem_len); | ||
1271 | retval = -EBUSY; | ||
1272 | goto err0; | ||
1273 | } | ||
1274 | |||
1275 | if (lock) | ||
1276 | mutex_lock(&fbi->mm_lock); | ||
1277 | fbi->fix.smem_start = addr; | ||
1278 | fbi->fix.smem_len = mem_len; | ||
1279 | if (lock) | ||
1280 | mutex_unlock(&fbi->mm_lock); | ||
1281 | |||
1282 | dev_dbg(fbi->device, "allocated fb @ p=0x%08x, v=0x%p, size=%d.\n", | ||
1283 | (uint32_t) fbi->fix.smem_start, fbi->screen_base, fbi->fix.smem_len); | ||
1284 | |||
1285 | fbi->screen_size = fbi->fix.smem_len; | ||
1286 | |||
1287 | /* Clear the screen */ | ||
1288 | memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); | ||
1289 | |||
1290 | return 0; | ||
1291 | |||
1292 | err0: | ||
1293 | fbi->fix.smem_len = 0; | ||
1294 | fbi->fix.smem_start = 0; | ||
1295 | fbi->screen_base = NULL; | ||
1296 | return retval; | ||
1297 | } | ||
1298 | |||
1299 | /** | ||
1300 | * mx3fb_unmap_video_memory() - de-allocate frame buffer memory. | ||
1301 | * @fbi: framebuffer information pointer | ||
1302 | * @return: error code indicating success or failure | ||
1303 | */ | ||
1304 | static int mx3fb_unmap_video_memory(struct fb_info *fbi) | ||
1305 | { | ||
1306 | dma_free_writecombine(fbi->device, fbi->fix.smem_len, | ||
1307 | fbi->screen_base, fbi->fix.smem_start); | ||
1308 | |||
1309 | fbi->screen_base = NULL; | ||
1310 | mutex_lock(&fbi->mm_lock); | ||
1311 | fbi->fix.smem_start = 0; | ||
1312 | fbi->fix.smem_len = 0; | ||
1313 | mutex_unlock(&fbi->mm_lock); | ||
1314 | return 0; | ||
1315 | } | ||
1316 | |||
1317 | /** | ||
1318 | * mx3fb_init_fbinfo() - initialize framebuffer information object. | ||
1319 | * @return: initialized framebuffer structure. | ||
1320 | */ | ||
1321 | static struct fb_info *mx3fb_init_fbinfo(struct device *dev, struct fb_ops *ops) | ||
1322 | { | ||
1323 | struct fb_info *fbi; | ||
1324 | struct mx3fb_info *mx3fbi; | ||
1325 | int ret; | ||
1326 | |||
1327 | /* Allocate sufficient memory for the fb structure */ | ||
1328 | fbi = framebuffer_alloc(sizeof(struct mx3fb_info), dev); | ||
1329 | if (!fbi) | ||
1330 | return NULL; | ||
1331 | |||
1332 | mx3fbi = fbi->par; | ||
1333 | mx3fbi->cookie = -EINVAL; | ||
1334 | mx3fbi->cur_ipu_buf = 0; | ||
1335 | |||
1336 | fbi->var.activate = FB_ACTIVATE_NOW; | ||
1337 | |||
1338 | fbi->fbops = ops; | ||
1339 | fbi->flags = FBINFO_FLAG_DEFAULT; | ||
1340 | fbi->pseudo_palette = mx3fbi->pseudo_palette; | ||
1341 | |||
1342 | mutex_init(&mx3fbi->mutex); | ||
1343 | |||
1344 | /* Allocate colormap */ | ||
1345 | ret = fb_alloc_cmap(&fbi->cmap, 16, 0); | ||
1346 | if (ret < 0) { | ||
1347 | framebuffer_release(fbi); | ||
1348 | return NULL; | ||
1349 | } | ||
1350 | |||
1351 | return fbi; | ||
1352 | } | ||
1353 | |||
1354 | static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan) | ||
1355 | { | ||
1356 | struct device *dev = mx3fb->dev; | ||
1357 | struct mx3fb_platform_data *mx3fb_pdata = dev_get_platdata(dev); | ||
1358 | const char *name = mx3fb_pdata->name; | ||
1359 | unsigned int irq; | ||
1360 | struct fb_info *fbi; | ||
1361 | struct mx3fb_info *mx3fbi; | ||
1362 | const struct fb_videomode *mode; | ||
1363 | int ret, num_modes; | ||
1364 | |||
1365 | if (mx3fb_pdata->disp_data_fmt >= ARRAY_SIZE(di_mappings)) { | ||
1366 | dev_err(dev, "Illegal display data format %d\n", | ||
1367 | mx3fb_pdata->disp_data_fmt); | ||
1368 | return -EINVAL; | ||
1369 | } | ||
1370 | |||
1371 | ichan->client = mx3fb; | ||
1372 | irq = ichan->eof_irq; | ||
1373 | |||
1374 | if (ichan->dma_chan.chan_id != IDMAC_SDC_0) | ||
1375 | return -EINVAL; | ||
1376 | |||
1377 | fbi = mx3fb_init_fbinfo(dev, &mx3fb_ops); | ||
1378 | if (!fbi) | ||
1379 | return -ENOMEM; | ||
1380 | |||
1381 | if (!fb_mode) | ||
1382 | fb_mode = name; | ||
1383 | |||
1384 | if (!fb_mode) { | ||
1385 | ret = -EINVAL; | ||
1386 | goto emode; | ||
1387 | } | ||
1388 | |||
1389 | if (mx3fb_pdata->mode && mx3fb_pdata->num_modes) { | ||
1390 | mode = mx3fb_pdata->mode; | ||
1391 | num_modes = mx3fb_pdata->num_modes; | ||
1392 | } else { | ||
1393 | mode = mx3fb_modedb; | ||
1394 | num_modes = ARRAY_SIZE(mx3fb_modedb); | ||
1395 | } | ||
1396 | |||
1397 | if (!fb_find_mode(&fbi->var, fbi, fb_mode, mode, | ||
1398 | num_modes, NULL, default_bpp)) { | ||
1399 | ret = -EBUSY; | ||
1400 | goto emode; | ||
1401 | } | ||
1402 | |||
1403 | fb_videomode_to_modelist(mode, num_modes, &fbi->modelist); | ||
1404 | |||
1405 | /* Default Y virtual size is 2x panel size */ | ||
1406 | fbi->var.yres_virtual = fbi->var.yres * 2; | ||
1407 | |||
1408 | mx3fb->fbi = fbi; | ||
1409 | |||
1410 | /* set Display Interface clock period */ | ||
1411 | mx3fb_write_reg(mx3fb, 0x00100010L, DI_HSP_CLK_PER); | ||
1412 | /* Might need to trigger HSP clock change - see 44.3.3.8.5 */ | ||
1413 | |||
1414 | sdc_set_brightness(mx3fb, 255); | ||
1415 | sdc_set_global_alpha(mx3fb, true, 0xFF); | ||
1416 | sdc_set_color_key(mx3fb, IDMAC_SDC_0, false, 0); | ||
1417 | |||
1418 | mx3fbi = fbi->par; | ||
1419 | mx3fbi->idmac_channel = ichan; | ||
1420 | mx3fbi->ipu_ch = ichan->dma_chan.chan_id; | ||
1421 | mx3fbi->mx3fb = mx3fb; | ||
1422 | mx3fbi->blank = FB_BLANK_NORMAL; | ||
1423 | |||
1424 | mx3fb->disp_data_fmt = mx3fb_pdata->disp_data_fmt; | ||
1425 | |||
1426 | init_completion(&mx3fbi->flip_cmpl); | ||
1427 | disable_irq(ichan->eof_irq); | ||
1428 | dev_dbg(mx3fb->dev, "disabling irq %d\n", ichan->eof_irq); | ||
1429 | ret = __set_par(fbi, false); | ||
1430 | if (ret < 0) | ||
1431 | goto esetpar; | ||
1432 | |||
1433 | __blank(FB_BLANK_UNBLANK, fbi); | ||
1434 | |||
1435 | dev_info(dev, "registered, using mode %s\n", fb_mode); | ||
1436 | |||
1437 | ret = register_framebuffer(fbi); | ||
1438 | if (ret < 0) | ||
1439 | goto erfb; | ||
1440 | |||
1441 | return 0; | ||
1442 | |||
1443 | erfb: | ||
1444 | esetpar: | ||
1445 | emode: | ||
1446 | fb_dealloc_cmap(&fbi->cmap); | ||
1447 | framebuffer_release(fbi); | ||
1448 | |||
1449 | return ret; | ||
1450 | } | ||
1451 | |||
1452 | static bool chan_filter(struct dma_chan *chan, void *arg) | ||
1453 | { | ||
1454 | struct dma_chan_request *rq = arg; | ||
1455 | struct device *dev; | ||
1456 | struct mx3fb_platform_data *mx3fb_pdata; | ||
1457 | |||
1458 | if (!imx_dma_is_ipu(chan)) | ||
1459 | return false; | ||
1460 | |||
1461 | if (!rq) | ||
1462 | return false; | ||
1463 | |||
1464 | dev = rq->mx3fb->dev; | ||
1465 | mx3fb_pdata = dev_get_platdata(dev); | ||
1466 | |||
1467 | return rq->id == chan->chan_id && | ||
1468 | mx3fb_pdata->dma_dev == chan->device->dev; | ||
1469 | } | ||
1470 | |||
1471 | static void release_fbi(struct fb_info *fbi) | ||
1472 | { | ||
1473 | mx3fb_unmap_video_memory(fbi); | ||
1474 | |||
1475 | fb_dealloc_cmap(&fbi->cmap); | ||
1476 | |||
1477 | unregister_framebuffer(fbi); | ||
1478 | framebuffer_release(fbi); | ||
1479 | } | ||
1480 | |||
1481 | static int mx3fb_probe(struct platform_device *pdev) | ||
1482 | { | ||
1483 | struct device *dev = &pdev->dev; | ||
1484 | int ret; | ||
1485 | struct resource *sdc_reg; | ||
1486 | struct mx3fb_data *mx3fb; | ||
1487 | dma_cap_mask_t mask; | ||
1488 | struct dma_chan *chan; | ||
1489 | struct dma_chan_request rq; | ||
1490 | |||
1491 | /* | ||
1492 | * Display Interface (DI) and Synchronous Display Controller (SDC) | ||
1493 | * registers | ||
1494 | */ | ||
1495 | sdc_reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1496 | if (!sdc_reg) | ||
1497 | return -EINVAL; | ||
1498 | |||
1499 | mx3fb = kzalloc(sizeof(*mx3fb), GFP_KERNEL); | ||
1500 | if (!mx3fb) | ||
1501 | return -ENOMEM; | ||
1502 | |||
1503 | spin_lock_init(&mx3fb->lock); | ||
1504 | |||
1505 | mx3fb->reg_base = ioremap(sdc_reg->start, resource_size(sdc_reg)); | ||
1506 | if (!mx3fb->reg_base) { | ||
1507 | ret = -ENOMEM; | ||
1508 | goto eremap; | ||
1509 | } | ||
1510 | |||
1511 | pr_debug("Remapped %pR at %p\n", sdc_reg, mx3fb->reg_base); | ||
1512 | |||
1513 | /* IDMAC interface */ | ||
1514 | dmaengine_get(); | ||
1515 | |||
1516 | mx3fb->dev = dev; | ||
1517 | platform_set_drvdata(pdev, mx3fb); | ||
1518 | |||
1519 | rq.mx3fb = mx3fb; | ||
1520 | |||
1521 | dma_cap_zero(mask); | ||
1522 | dma_cap_set(DMA_SLAVE, mask); | ||
1523 | dma_cap_set(DMA_PRIVATE, mask); | ||
1524 | rq.id = IDMAC_SDC_0; | ||
1525 | chan = dma_request_channel(mask, chan_filter, &rq); | ||
1526 | if (!chan) { | ||
1527 | ret = -EBUSY; | ||
1528 | goto ersdc0; | ||
1529 | } | ||
1530 | |||
1531 | mx3fb->backlight_level = 255; | ||
1532 | |||
1533 | ret = init_fb_chan(mx3fb, to_idmac_chan(chan)); | ||
1534 | if (ret < 0) | ||
1535 | goto eisdc0; | ||
1536 | |||
1537 | return 0; | ||
1538 | |||
1539 | eisdc0: | ||
1540 | dma_release_channel(chan); | ||
1541 | ersdc0: | ||
1542 | dmaengine_put(); | ||
1543 | iounmap(mx3fb->reg_base); | ||
1544 | eremap: | ||
1545 | kfree(mx3fb); | ||
1546 | dev_err(dev, "mx3fb: failed to register fb\n"); | ||
1547 | return ret; | ||
1548 | } | ||
1549 | |||
1550 | static int mx3fb_remove(struct platform_device *dev) | ||
1551 | { | ||
1552 | struct mx3fb_data *mx3fb = platform_get_drvdata(dev); | ||
1553 | struct fb_info *fbi = mx3fb->fbi; | ||
1554 | struct mx3fb_info *mx3_fbi = fbi->par; | ||
1555 | struct dma_chan *chan; | ||
1556 | |||
1557 | chan = &mx3_fbi->idmac_channel->dma_chan; | ||
1558 | release_fbi(fbi); | ||
1559 | |||
1560 | dma_release_channel(chan); | ||
1561 | dmaengine_put(); | ||
1562 | |||
1563 | iounmap(mx3fb->reg_base); | ||
1564 | kfree(mx3fb); | ||
1565 | return 0; | ||
1566 | } | ||
1567 | |||
1568 | static struct platform_driver mx3fb_driver = { | ||
1569 | .driver = { | ||
1570 | .name = MX3FB_NAME, | ||
1571 | .owner = THIS_MODULE, | ||
1572 | }, | ||
1573 | .probe = mx3fb_probe, | ||
1574 | .remove = mx3fb_remove, | ||
1575 | .suspend = mx3fb_suspend, | ||
1576 | .resume = mx3fb_resume, | ||
1577 | }; | ||
1578 | |||
1579 | /* | ||
1580 | * Parse user specified options (`video=mx3fb:') | ||
1581 | * example: | ||
1582 | * video=mx3fb:bpp=16 | ||
1583 | */ | ||
1584 | static int __init mx3fb_setup(void) | ||
1585 | { | ||
1586 | #ifndef MODULE | ||
1587 | char *opt, *options = NULL; | ||
1588 | |||
1589 | if (fb_get_options("mx3fb", &options)) | ||
1590 | return -ENODEV; | ||
1591 | |||
1592 | if (!options || !*options) | ||
1593 | return 0; | ||
1594 | |||
1595 | while ((opt = strsep(&options, ",")) != NULL) { | ||
1596 | if (!*opt) | ||
1597 | continue; | ||
1598 | if (!strncmp(opt, "bpp=", 4)) | ||
1599 | default_bpp = simple_strtoul(opt + 4, NULL, 0); | ||
1600 | else | ||
1601 | fb_mode = opt; | ||
1602 | } | ||
1603 | #endif | ||
1604 | |||
1605 | return 0; | ||
1606 | } | ||
1607 | |||
1608 | static int __init mx3fb_init(void) | ||
1609 | { | ||
1610 | int ret = mx3fb_setup(); | ||
1611 | |||
1612 | if (ret < 0) | ||
1613 | return ret; | ||
1614 | |||
1615 | ret = platform_driver_register(&mx3fb_driver); | ||
1616 | return ret; | ||
1617 | } | ||
1618 | |||
1619 | static void __exit mx3fb_exit(void) | ||
1620 | { | ||
1621 | platform_driver_unregister(&mx3fb_driver); | ||
1622 | } | ||
1623 | |||
1624 | module_init(mx3fb_init); | ||
1625 | module_exit(mx3fb_exit); | ||
1626 | |||
1627 | MODULE_AUTHOR("Freescale Semiconductor, Inc."); | ||
1628 | MODULE_DESCRIPTION("MX3 framebuffer driver"); | ||
1629 | MODULE_ALIAS("platform:" MX3FB_NAME); | ||
1630 | MODULE_LICENSE("GPL v2"); | ||