diff options
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/sh_mipi_dsi.c | 32 | ||||
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 629 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 348 | ||||
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.h | 41 |
4 files changed, 691 insertions, 359 deletions
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 5699ce0c1780..3f3d431033ca 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c | |||
@@ -123,83 +123,87 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, | |||
123 | u32 linelength; | 123 | u32 linelength; |
124 | bool yuv; | 124 | bool yuv; |
125 | 125 | ||
126 | /* Select data format */ | 126 | /* |
127 | * Select data format. MIPI DSI is not hot-pluggable, so, we just use | ||
128 | * the default videomode. If this ever becomes a problem, We'll have to | ||
129 | * move this to mipi_display_on() above and use info->var.xres | ||
130 | */ | ||
127 | switch (pdata->data_format) { | 131 | switch (pdata->data_format) { |
128 | case MIPI_RGB888: | 132 | case MIPI_RGB888: |
129 | pctype = 0; | 133 | pctype = 0; |
130 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; | 134 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; |
131 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | 135 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; |
132 | linelength = ch->lcd_cfg.xres * 3; | 136 | linelength = ch->lcd_cfg[0].xres * 3; |
133 | yuv = false; | 137 | yuv = false; |
134 | break; | 138 | break; |
135 | case MIPI_RGB565: | 139 | case MIPI_RGB565: |
136 | pctype = 1; | 140 | pctype = 1; |
137 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; | 141 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; |
138 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | 142 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; |
139 | linelength = ch->lcd_cfg.xres * 2; | 143 | linelength = ch->lcd_cfg[0].xres * 2; |
140 | yuv = false; | 144 | yuv = false; |
141 | break; | 145 | break; |
142 | case MIPI_RGB666_LP: | 146 | case MIPI_RGB666_LP: |
143 | pctype = 2; | 147 | pctype = 2; |
144 | datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; | 148 | datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; |
145 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | 149 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; |
146 | linelength = ch->lcd_cfg.xres * 3; | 150 | linelength = ch->lcd_cfg[0].xres * 3; |
147 | yuv = false; | 151 | yuv = false; |
148 | break; | 152 | break; |
149 | case MIPI_RGB666: | 153 | case MIPI_RGB666: |
150 | pctype = 3; | 154 | pctype = 3; |
151 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; | 155 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; |
152 | pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; | 156 | pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; |
153 | linelength = (ch->lcd_cfg.xres * 18 + 7) / 8; | 157 | linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; |
154 | yuv = false; | 158 | yuv = false; |
155 | break; | 159 | break; |
156 | case MIPI_BGR888: | 160 | case MIPI_BGR888: |
157 | pctype = 8; | 161 | pctype = 8; |
158 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; | 162 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; |
159 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | 163 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; |
160 | linelength = ch->lcd_cfg.xres * 3; | 164 | linelength = ch->lcd_cfg[0].xres * 3; |
161 | yuv = false; | 165 | yuv = false; |
162 | break; | 166 | break; |
163 | case MIPI_BGR565: | 167 | case MIPI_BGR565: |
164 | pctype = 9; | 168 | pctype = 9; |
165 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; | 169 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; |
166 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | 170 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; |
167 | linelength = ch->lcd_cfg.xres * 2; | 171 | linelength = ch->lcd_cfg[0].xres * 2; |
168 | yuv = false; | 172 | yuv = false; |
169 | break; | 173 | break; |
170 | case MIPI_BGR666_LP: | 174 | case MIPI_BGR666_LP: |
171 | pctype = 0xa; | 175 | pctype = 0xa; |
172 | datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; | 176 | datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; |
173 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; | 177 | pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; |
174 | linelength = ch->lcd_cfg.xres * 3; | 178 | linelength = ch->lcd_cfg[0].xres * 3; |
175 | yuv = false; | 179 | yuv = false; |
176 | break; | 180 | break; |
177 | case MIPI_BGR666: | 181 | case MIPI_BGR666: |
178 | pctype = 0xb; | 182 | pctype = 0xb; |
179 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; | 183 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; |
180 | pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; | 184 | pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; |
181 | linelength = (ch->lcd_cfg.xres * 18 + 7) / 8; | 185 | linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; |
182 | yuv = false; | 186 | yuv = false; |
183 | break; | 187 | break; |
184 | case MIPI_YUYV: | 188 | case MIPI_YUYV: |
185 | pctype = 4; | 189 | pctype = 4; |
186 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; | 190 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; |
187 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | 191 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; |
188 | linelength = ch->lcd_cfg.xres * 2; | 192 | linelength = ch->lcd_cfg[0].xres * 2; |
189 | yuv = true; | 193 | yuv = true; |
190 | break; | 194 | break; |
191 | case MIPI_UYVY: | 195 | case MIPI_UYVY: |
192 | pctype = 5; | 196 | pctype = 5; |
193 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; | 197 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; |
194 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; | 198 | pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; |
195 | linelength = ch->lcd_cfg.xres * 2; | 199 | linelength = ch->lcd_cfg[0].xres * 2; |
196 | yuv = true; | 200 | yuv = true; |
197 | break; | 201 | break; |
198 | case MIPI_YUV420_L: | 202 | case MIPI_YUV420_L: |
199 | pctype = 6; | 203 | pctype = 6; |
200 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; | 204 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; |
201 | pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; | 205 | pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; |
202 | linelength = (ch->lcd_cfg.xres * 12 + 7) / 8; | 206 | linelength = (ch->lcd_cfg[0].xres * 12 + 7) / 8; |
203 | yuv = true; | 207 | yuv = true; |
204 | break; | 208 | break; |
205 | case MIPI_YUV420: | 209 | case MIPI_YUV420: |
@@ -207,7 +211,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, | |||
207 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; | 211 | datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; |
208 | pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; | 212 | pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; |
209 | /* Length of U/V line */ | 213 | /* Length of U/V line */ |
210 | linelength = (ch->lcd_cfg.xres + 1) / 2; | 214 | linelength = (ch->lcd_cfg[0].xres + 1) / 2; |
211 | yuv = true; | 215 | yuv = true; |
212 | break; | 216 | break; |
213 | default: | 217 | default: |
@@ -281,7 +285,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, | |||
281 | iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */ | 285 | iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */ |
282 | /* | 286 | /* |
283 | * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see | 287 | * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see |
284 | * sh_mobile_lcdc_info.ch[0].lcd_cfg.xres), HSALEN = 1 - default | 288 | * sh_mobile_lcdc_info.ch[0].lcd_cfg[0].xres), HSALEN = 1 - default |
285 | * (unused, since VMCTR2[HSABM] = 0) | 289 | * (unused, since VMCTR2[HSABM] = 0) |
286 | */ | 290 | */ |
287 | iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */ | 291 | iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */ |
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index ef989d94511c..55b3077ff6ff 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -28,6 +28,8 @@ | |||
28 | #include <video/sh_mobile_hdmi.h> | 28 | #include <video/sh_mobile_hdmi.h> |
29 | #include <video/sh_mobile_lcdc.h> | 29 | #include <video/sh_mobile_lcdc.h> |
30 | 30 | ||
31 | #include "sh_mobile_lcdcfb.h" | ||
32 | |||
31 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ | 33 | #define HDMI_SYSTEM_CTRL 0x00 /* System control */ |
32 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, | 34 | #define HDMI_L_R_DATA_SWAP_CTRL_RPKT 0x01 /* L/R data swap control, |
33 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ | 35 | bits 19..16 of 20-bit N for Audio Clock Regeneration packet */ |
@@ -206,12 +208,15 @@ enum hotplug_state { | |||
206 | 208 | ||
207 | struct sh_hdmi { | 209 | struct sh_hdmi { |
208 | void __iomem *base; | 210 | void __iomem *base; |
209 | enum hotplug_state hp_state; | 211 | enum hotplug_state hp_state; /* hot-plug status */ |
212 | bool preprogrammed_mode; /* use a pre-programmed VIC or the external mode */ | ||
210 | struct clk *hdmi_clk; | 213 | struct clk *hdmi_clk; |
211 | struct device *dev; | 214 | struct device *dev; |
212 | struct fb_info *info; | 215 | struct fb_info *info; |
216 | struct mutex mutex; /* Protect the info pointer */ | ||
213 | struct delayed_work edid_work; | 217 | struct delayed_work edid_work; |
214 | struct fb_var_screeninfo var; | 218 | struct fb_var_screeninfo var; |
219 | struct fb_monspecs monspec; | ||
215 | }; | 220 | }; |
216 | 221 | ||
217 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) | 222 | static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) |
@@ -277,7 +282,7 @@ static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = { | |||
277 | */ | 282 | */ |
278 | 283 | ||
279 | /* External video parameter settings */ | 284 | /* External video parameter settings */ |
280 | static void hdmi_external_video_param(struct sh_hdmi *hdmi) | 285 | static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) |
281 | { | 286 | { |
282 | struct fb_var_screeninfo *var = &hdmi->var; | 287 | struct fb_var_screeninfo *var = &hdmi->var; |
283 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; | 288 | u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; |
@@ -309,9 +314,9 @@ static void hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
309 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) | 314 | if (var->sync & FB_SYNC_VERT_HIGH_ACT) |
310 | sync |= 8; | 315 | sync |= 8; |
311 | 316 | ||
312 | pr_debug("H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", | 317 | dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", |
313 | htotal, hblank, hdelay, var->hsync_len, | 318 | htotal, hblank, hdelay, var->hsync_len, |
314 | vtotal, vblank, vdelay, var->vsync_len, sync); | 319 | vtotal, vblank, vdelay, var->vsync_len, sync); |
315 | 320 | ||
316 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | 321 | hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); |
317 | 322 | ||
@@ -336,7 +341,10 @@ static void hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
336 | 341 | ||
337 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); | 342 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); |
338 | 343 | ||
339 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for manual mode */ | 344 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ |
345 | if (!hdmi->preprogrammed_mode) | ||
346 | hdmi_write(hdmi, sync | 1 | (voffset << 4), | ||
347 | HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | ||
340 | } | 348 | } |
341 | 349 | ||
342 | /** | 350 | /** |
@@ -454,21 +462,61 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) | |||
454 | } | 462 | } |
455 | 463 | ||
456 | /** | 464 | /** |
457 | * sh_hdmi_phy_config() | 465 | * sh_hdmi_phy_config() - configure the HDMI PHY for the used video mode |
458 | */ | 466 | */ |
459 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | 467 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) |
460 | { | 468 | { |
461 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ | 469 | if (hdmi->var.yres > 480) { |
462 | hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | 470 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ |
463 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | 471 | /* |
464 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | 472 | * [1:0] Speed_A |
465 | /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ | 473 | * [3:2] Speed_B |
466 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | 474 | * [4] PLLA_Bypass |
467 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | 475 | * [6] DRV_TEST_EN |
468 | hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | 476 | * [7] DRV_TEST_IN |
469 | hdmi_write(hdmi, 0x0E, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | 477 | */ |
470 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | 478 | hdmi_write(hdmi, 0x0f, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); |
471 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | 479 | /* PLLB_CONFIG[17], PLLA_CONFIG[17] - not in PHY datasheet */ |
480 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | ||
481 | /* | ||
482 | * [2:0] BGR_I_OFFSET | ||
483 | * [6:4] BGR_V_OFFSET | ||
484 | */ | ||
485 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | ||
486 | /* PLLA_CONFIG[7:0]: VCO gain, VCO offset, LPF resistance[0] */ | ||
487 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | ||
488 | /* | ||
489 | * PLLA_CONFIG[15:8]: regulator voltage[0], CP current, | ||
490 | * LPF capacitance, LPF resistance[1] | ||
491 | */ | ||
492 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | ||
493 | /* PLLB_CONFIG[7:0]: LPF resistance[0], VCO offset, VCO gain */ | ||
494 | hdmi_write(hdmi, 0x4A, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | ||
495 | /* | ||
496 | * PLLB_CONFIG[15:8]: regulator voltage[0], CP current, | ||
497 | * LPF capacitance, LPF resistance[1] | ||
498 | */ | ||
499 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | ||
500 | /* DRV_CONFIG, PE_CONFIG */ | ||
501 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | ||
502 | /* | ||
503 | * [2:0] AMON_SEL (4 == LPF voltage) | ||
504 | * [4] PLLA_CONFIG[16] | ||
505 | * [5] PLLB_CONFIG[16] | ||
506 | */ | ||
507 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | ||
508 | } else { | ||
509 | /* for 480p8bit 27MHz */ | ||
510 | hdmi_write(hdmi, 0x19, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | ||
511 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | ||
512 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | ||
513 | hdmi_write(hdmi, 0x44, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | ||
514 | hdmi_write(hdmi, 0x32, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | ||
515 | hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | ||
516 | hdmi_write(hdmi, 0x0F, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | ||
517 | hdmi_write(hdmi, 0x20, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | ||
518 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | ||
519 | } | ||
472 | } | 520 | } |
473 | 521 | ||
474 | /** | 522 | /** |
@@ -476,6 +524,8 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | |||
476 | */ | 524 | */ |
477 | static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | 525 | static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) |
478 | { | 526 | { |
527 | u8 vic; | ||
528 | |||
479 | /* AVI InfoFrame */ | 529 | /* AVI InfoFrame */ |
480 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); | 530 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_INDEX); |
481 | 531 | ||
@@ -500,9 +550,9 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | |||
500 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); | 550 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB1); |
501 | 551 | ||
502 | /* | 552 | /* |
503 | * C = No Data | 553 | * [7:6] C = Colorimetry: no data |
504 | * M = 16:9 Picture Aspect Ratio | 554 | * [5:4] M = 2: 16:9, 1: 4:3 Picture Aspect Ratio |
505 | * R = Same as picture aspect ratio | 555 | * [3:0] R = 8: Active Frame Aspect Ratio: same as picture aspect ratio |
506 | */ | 556 | */ |
507 | hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); | 557 | hdmi_write(hdmi, 0x28, HDMI_CTRL_PKT_BUF_ACCESS_PB2); |
508 | 558 | ||
@@ -516,9 +566,15 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | |||
516 | 566 | ||
517 | /* | 567 | /* |
518 | * VIC = 1280 x 720p: ignored if external config is used | 568 | * VIC = 1280 x 720p: ignored if external config is used |
519 | * Send 2 for 720 x 480p, 16 for 1080p | 569 | * Send 2 for 720 x 480p, 16 for 1080p, ignored in external mode |
520 | */ | 570 | */ |
521 | hdmi_write(hdmi, 4, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | 571 | if (hdmi->var.yres == 1080 && hdmi->var.xres == 1920) |
572 | vic = 16; | ||
573 | else if (hdmi->var.yres == 480 && hdmi->var.xres == 720) | ||
574 | vic = 2; | ||
575 | else | ||
576 | vic = 4; | ||
577 | hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | ||
522 | 578 | ||
523 | /* PR = No Repetition */ | 579 | /* PR = No Repetition */ |
524 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); | 580 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB5); |
@@ -592,100 +648,6 @@ static void sh_hdmi_audio_infoframe_setup(struct sh_hdmi *hdmi) | |||
592 | } | 648 | } |
593 | 649 | ||
594 | /** | 650 | /** |
595 | * sh_hdmi_gamut_metadata_setup() - Gamut Metadata Packet of CONTROL PACKET | ||
596 | */ | ||
597 | static void sh_hdmi_gamut_metadata_setup(struct sh_hdmi *hdmi) | ||
598 | { | ||
599 | int i; | ||
600 | |||
601 | /* Gamut Metadata Packet */ | ||
602 | hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_INDEX); | ||
603 | |||
604 | /* Packet Type = 0x0A */ | ||
605 | hdmi_write(hdmi, 0x0A, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
606 | /* Gamut Packet is not used, so default value */ | ||
607 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
608 | /* Gamut Packet is not used, so default value */ | ||
609 | hdmi_write(hdmi, 0x10, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
610 | |||
611 | /* GBD bytes 0 through 27 */ | ||
612 | for (i = 0; i <= 27; i++) | ||
613 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0_63H - PB27_7EH */ | ||
614 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
615 | } | ||
616 | |||
617 | /** | ||
618 | * sh_hdmi_acp_setup() - Audio Content Protection Packet (ACP) | ||
619 | */ | ||
620 | static void sh_hdmi_acp_setup(struct sh_hdmi *hdmi) | ||
621 | { | ||
622 | int i; | ||
623 | |||
624 | /* Audio Content Protection Packet (ACP) */ | ||
625 | hdmi_write(hdmi, 0x01, HDMI_CTRL_PKT_BUF_INDEX); | ||
626 | |||
627 | /* Packet Type = 0x04 */ | ||
628 | hdmi_write(hdmi, 0x04, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
629 | /* ACP_Type */ | ||
630 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
631 | /* Reserved (0) */ | ||
632 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
633 | |||
634 | /* GBD bytes 0 through 27 */ | ||
635 | for (i = 0; i <= 27; i++) | ||
636 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
637 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
638 | } | ||
639 | |||
640 | /** | ||
641 | * sh_hdmi_isrc1_setup() - ISRC1 Packet | ||
642 | */ | ||
643 | static void sh_hdmi_isrc1_setup(struct sh_hdmi *hdmi) | ||
644 | { | ||
645 | int i; | ||
646 | |||
647 | /* ISRC1 Packet */ | ||
648 | hdmi_write(hdmi, 0x02, HDMI_CTRL_PKT_BUF_INDEX); | ||
649 | |||
650 | /* Packet Type = 0x05 */ | ||
651 | hdmi_write(hdmi, 0x05, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
652 | /* ISRC_Cont, ISRC_Valid, Reserved (0), ISRC_Status */ | ||
653 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
654 | /* Reserved (0) */ | ||
655 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
656 | |||
657 | /* PB0 UPC_EAN_ISRC_0-15 */ | ||
658 | /* Bytes PB16-PB27 shall be set to a value of 0. */ | ||
659 | for (i = 0; i <= 27; i++) | ||
660 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
661 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
662 | } | ||
663 | |||
664 | /** | ||
665 | * sh_hdmi_isrc2_setup() - ISRC2 Packet | ||
666 | */ | ||
667 | static void sh_hdmi_isrc2_setup(struct sh_hdmi *hdmi) | ||
668 | { | ||
669 | int i; | ||
670 | |||
671 | /* ISRC2 Packet */ | ||
672 | hdmi_write(hdmi, 0x03, HDMI_CTRL_PKT_BUF_INDEX); | ||
673 | |||
674 | /* HB0 Packet Type = 0x06 */ | ||
675 | hdmi_write(hdmi, 0x06, HDMI_CTRL_PKT_BUF_ACCESS_HB0); | ||
676 | /* Reserved (0) */ | ||
677 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB1); | ||
678 | /* Reserved (0) */ | ||
679 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_HB2); | ||
680 | |||
681 | /* PB0 UPC_EAN_ISRC_16-31 */ | ||
682 | /* Bytes PB16-PB27 shall be set to a value of 0. */ | ||
683 | for (i = 0; i <= 27; i++) | ||
684 | /* HDMI_CTRL_PKT_BUF_ACCESS_PB0 - PB27 */ | ||
685 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB0 + i); | ||
686 | } | ||
687 | |||
688 | /** | ||
689 | * sh_hdmi_configure() - Initialise HDMI for output | 651 | * sh_hdmi_configure() - Initialise HDMI for output |
690 | */ | 652 | */ |
691 | static void sh_hdmi_configure(struct sh_hdmi *hdmi) | 653 | static void sh_hdmi_configure(struct sh_hdmi *hdmi) |
@@ -705,18 +667,6 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) | |||
705 | /* Audio InfoFrame */ | 667 | /* Audio InfoFrame */ |
706 | sh_hdmi_audio_infoframe_setup(hdmi); | 668 | sh_hdmi_audio_infoframe_setup(hdmi); |
707 | 669 | ||
708 | /* Gamut Metadata packet */ | ||
709 | sh_hdmi_gamut_metadata_setup(hdmi); | ||
710 | |||
711 | /* Audio Content Protection (ACP) Packet */ | ||
712 | sh_hdmi_acp_setup(hdmi); | ||
713 | |||
714 | /* ISRC1 Packet */ | ||
715 | sh_hdmi_isrc1_setup(hdmi); | ||
716 | |||
717 | /* ISRC2 Packet */ | ||
718 | sh_hdmi_isrc2_setup(hdmi); | ||
719 | |||
720 | /* | 670 | /* |
721 | * Control packet auto send with VSYNC control: auto send | 671 | * Control packet auto send with VSYNC control: auto send |
722 | * General control, Gamut metadata, ISRC, and ACP packets | 672 | * General control, Gamut metadata, ISRC, and ACP packets |
@@ -734,17 +684,42 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) | |||
734 | hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); | 684 | hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); |
735 | } | 685 | } |
736 | 686 | ||
737 | static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) | 687 | static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, |
688 | const struct fb_videomode *mode) | ||
738 | { | 689 | { |
739 | struct fb_var_screeninfo *var = &hdmi->var; | 690 | long target = PICOS2KHZ(mode->pixclock) * 1000, |
740 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 691 | rate = clk_round_rate(hdmi->hdmi_clk, target); |
741 | struct fb_videomode *lcd_cfg = &pdata->lcd_chan->lcd_cfg; | 692 | unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX; |
742 | unsigned long height = var->height, width = var->width; | 693 | |
743 | int i; | 694 | dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", |
695 | mode->left_margin, mode->xres, | ||
696 | mode->right_margin, mode->hsync_len, | ||
697 | mode->upper_margin, mode->yres, | ||
698 | mode->lower_margin, mode->vsync_len); | ||
699 | |||
700 | dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target, | ||
701 | rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, | ||
702 | mode->refresh); | ||
703 | |||
704 | return rate_error; | ||
705 | } | ||
706 | |||
707 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | ||
708 | { | ||
709 | struct fb_var_screeninfo tmpvar; | ||
710 | struct fb_var_screeninfo *var = &tmpvar; | ||
711 | const struct fb_videomode *mode, *found = NULL; | ||
712 | struct fb_info *info = hdmi->info; | ||
713 | struct fb_modelist *modelist = NULL; | ||
714 | unsigned int f_width = 0, f_height = 0, f_refresh = 0; | ||
715 | unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ | ||
716 | bool exact_match = false; | ||
744 | u8 edid[128]; | 717 | u8 edid[128]; |
718 | char *forced; | ||
719 | int i; | ||
745 | 720 | ||
746 | /* Read EDID */ | 721 | /* Read EDID */ |
747 | pr_debug("Read back EDID code:"); | 722 | dev_dbg(hdmi->dev, "Read back EDID code:"); |
748 | for (i = 0; i < 128; i++) { | 723 | for (i = 0; i < 128; i++) { |
749 | edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); | 724 | edid[i] = hdmi_read(hdmi, HDMI_EDID_KSV_FIFO_ACCESS_WINDOW); |
750 | #ifdef DEBUG | 725 | #ifdef DEBUG |
@@ -759,29 +734,97 @@ static void sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
759 | #ifdef DEBUG | 734 | #ifdef DEBUG |
760 | printk(KERN_CONT "\n"); | 735 | printk(KERN_CONT "\n"); |
761 | #endif | 736 | #endif |
762 | fb_parse_edid(edid, var); | 737 | |
763 | pr_debug("%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n", | 738 | fb_edid_to_monspecs(edid, &hdmi->monspec); |
764 | var->left_margin, var->xres, var->right_margin, var->hsync_len, | 739 | |
765 | var->upper_margin, var->yres, var->lower_margin, var->vsync_len, | 740 | fb_get_options("sh_mobile_lcdc", &forced); |
766 | PICOS2KHZ(var->pixclock)); | 741 | if (forced && *forced) { |
767 | 742 | /* Only primitive parsing so far */ | |
768 | /* FIXME: Use user-provided configuration instead of EDID */ | 743 | i = sscanf(forced, "%ux%u@%u", |
769 | var->width = width; | 744 | &f_width, &f_height, &f_refresh); |
770 | var->xres = lcd_cfg->xres; | 745 | if (i < 2) { |
771 | var->xres_virtual = lcd_cfg->xres; | 746 | f_width = 0; |
772 | var->left_margin = lcd_cfg->left_margin; | 747 | f_height = 0; |
773 | var->right_margin = lcd_cfg->right_margin; | 748 | } |
774 | var->hsync_len = lcd_cfg->hsync_len; | 749 | dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n", |
775 | var->height = height; | 750 | f_width, f_height, f_refresh); |
776 | var->yres = lcd_cfg->yres; | 751 | } |
777 | var->yres_virtual = lcd_cfg->yres * 2; | 752 | |
778 | var->upper_margin = lcd_cfg->upper_margin; | 753 | /* Walk monitor modes to find the best or the exact match */ |
779 | var->lower_margin = lcd_cfg->lower_margin; | 754 | for (i = 0, mode = hdmi->monspec.modedb; |
780 | var->vsync_len = lcd_cfg->vsync_len; | 755 | f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; |
781 | var->sync = lcd_cfg->sync; | 756 | i++, mode++) { |
782 | var->pixclock = lcd_cfg->pixclock; | 757 | unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); |
783 | 758 | ||
784 | hdmi_external_video_param(hdmi); | 759 | /* No interest in unmatching modes */ |
760 | if (f_width != mode->xres || f_height != mode->yres) | ||
761 | continue; | ||
762 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) | ||
763 | /* | ||
764 | * Exact match if either the refresh rate matches or it | ||
765 | * hasn't been specified and we've found a mode, for | ||
766 | * which we can configure the clock precisely | ||
767 | */ | ||
768 | exact_match = true; | ||
769 | else if (found && found_rate_error <= rate_error) | ||
770 | /* | ||
771 | * We otherwise search for the closest matching clock | ||
772 | * rate - either if no refresh rate has been specified | ||
773 | * or we cannot find an exactly matching one | ||
774 | */ | ||
775 | continue; | ||
776 | |||
777 | /* Check if supported: sufficient fb memory, supported clock-rate */ | ||
778 | fb_videomode_to_var(var, mode); | ||
779 | |||
780 | if (info && info->fbops->fb_check_var && | ||
781 | info->fbops->fb_check_var(var, info)) { | ||
782 | exact_match = false; | ||
783 | continue; | ||
784 | } | ||
785 | |||
786 | found = mode; | ||
787 | found_rate_error = rate_error; | ||
788 | } | ||
789 | |||
790 | /* | ||
791 | * TODO 1: if no ->info is present, postpone running the config until | ||
792 | * after ->info first gets registered. | ||
793 | * TODO 2: consider registering the HDMI platform device from the LCDC | ||
794 | * driver, and passing ->info with HDMI platform data. | ||
795 | */ | ||
796 | if (info && !found) { | ||
797 | modelist = hdmi->info->modelist.next && | ||
798 | !list_empty(&hdmi->info->modelist) ? | ||
799 | list_entry(hdmi->info->modelist.next, | ||
800 | struct fb_modelist, list) : | ||
801 | NULL; | ||
802 | |||
803 | if (modelist) { | ||
804 | found = &modelist->mode; | ||
805 | found_rate_error = sh_hdmi_rate_error(hdmi, found); | ||
806 | } | ||
807 | } | ||
808 | |||
809 | /* No cookie today */ | ||
810 | if (!found) | ||
811 | return -ENXIO; | ||
812 | |||
813 | dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", | ||
814 | modelist ? "default" : "EDID", found->xres, found->yres, | ||
815 | found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error); | ||
816 | |||
817 | if ((found->xres == 720 && found->yres == 480) || | ||
818 | (found->xres == 1280 && found->yres == 720) || | ||
819 | (found->xres == 1920 && found->yres == 1080)) | ||
820 | hdmi->preprogrammed_mode = true; | ||
821 | else | ||
822 | hdmi->preprogrammed_mode = false; | ||
823 | |||
824 | fb_videomode_to_var(&hdmi->var, found); | ||
825 | sh_hdmi_external_video_param(hdmi); | ||
826 | |||
827 | return 0; | ||
785 | } | 828 | } |
786 | 829 | ||
787 | static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | 830 | static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) |
@@ -809,8 +852,8 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
809 | hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); | 852 | hdmi_write(hdmi, 0xFF, HDMI_INTERRUPT_STATUS_2); |
810 | 853 | ||
811 | if (printk_ratelimit()) | 854 | if (printk_ratelimit()) |
812 | pr_debug("IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", | 855 | dev_dbg(hdmi->dev, "IRQ #%d: Status #1: 0x%x & 0x%x, #2: 0x%x & 0x%x\n", |
813 | irq, status1, mask1, status2, mask2); | 856 | irq, status1, mask1, status2, mask2); |
814 | 857 | ||
815 | if (!((status1 & mask1) | (status2 & mask2))) { | 858 | if (!((status1 & mask1) | (status2 & mask2))) { |
816 | return IRQ_NONE; | 859 | return IRQ_NONE; |
@@ -821,7 +864,7 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
821 | udelay(500); | 864 | udelay(500); |
822 | 865 | ||
823 | msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); | 866 | msens = hdmi_read(hdmi, HDMI_HOT_PLUG_MSENS_STATUS); |
824 | pr_debug("MSENS 0x%x\n", msens); | 867 | dev_dbg(hdmi->dev, "MSENS 0x%x\n", msens); |
825 | /* Check, if hot plug & MSENS pin status are both high */ | 868 | /* Check, if hot plug & MSENS pin status are both high */ |
826 | if ((msens & 0xC0) == 0xC0) { | 869 | if ((msens & 0xC0) == 0xC0) { |
827 | /* Display plug in */ | 870 | /* Display plug in */ |
@@ -857,83 +900,176 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
857 | return IRQ_HANDLED; | 900 | return IRQ_HANDLED; |
858 | } | 901 | } |
859 | 902 | ||
860 | static void hdmi_display_on(void *arg, struct fb_info *info) | 903 | /* locking: called with info->lock held, or before register_framebuffer() */ |
904 | static void sh_hdmi_display_on(void *arg, struct fb_info *info) | ||
861 | { | 905 | { |
906 | /* | ||
907 | * info is guaranteed to be valid, when we are called, because our | ||
908 | * FB_EVENT_FB_UNBIND notify is also called with info->lock held | ||
909 | */ | ||
862 | struct sh_hdmi *hdmi = arg; | 910 | struct sh_hdmi *hdmi = arg; |
863 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 911 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
912 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
864 | 913 | ||
865 | if (info->var.xres != 1280 || info->var.yres != 720) { | 914 | dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, |
866 | dev_warn(info->device, "Unsupported framebuffer geometry %ux%u\n", | 915 | pdata->lcd_dev, info->state); |
867 | info->var.xres, info->var.yres); | 916 | |
868 | return; | 917 | /* No need to lock */ |
869 | } | 918 | hdmi->info = info; |
870 | 919 | ||
871 | pr_debug("%s(%p): state %x\n", __func__, pdata->lcd_dev, info->state); | ||
872 | /* | 920 | /* |
873 | * FIXME: not a good place to store fb_info. And we cannot nullify it | 921 | * hp_state can be set to |
874 | * even on monitor disconnect. What should the lifecycle be? | 922 | * HDMI_HOTPLUG_DISCONNECTED: on monitor unplug |
923 | * HDMI_HOTPLUG_CONNECTED: on monitor plug-in | ||
924 | * HDMI_HOTPLUG_EDID_DONE: on EDID read completion | ||
875 | */ | 925 | */ |
876 | hdmi->info = info; | ||
877 | switch (hdmi->hp_state) { | 926 | switch (hdmi->hp_state) { |
878 | case HDMI_HOTPLUG_EDID_DONE: | 927 | case HDMI_HOTPLUG_EDID_DONE: |
879 | /* PS mode d->e. All functions are active */ | 928 | /* PS mode d->e. All functions are active */ |
880 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); | 929 | hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); |
881 | pr_debug("HDMI running\n"); | 930 | dev_dbg(hdmi->dev, "HDMI running\n"); |
882 | break; | 931 | break; |
883 | case HDMI_HOTPLUG_DISCONNECTED: | 932 | case HDMI_HOTPLUG_DISCONNECTED: |
884 | info->state = FBINFO_STATE_SUSPENDED; | 933 | info->state = FBINFO_STATE_SUSPENDED; |
885 | default: | 934 | default: |
886 | hdmi->var = info->var; | 935 | hdmi->var = ch->display_var; |
887 | } | 936 | } |
888 | } | 937 | } |
889 | 938 | ||
890 | static void hdmi_display_off(void *arg) | 939 | /* locking: called with info->lock held */ |
940 | static void sh_hdmi_display_off(void *arg) | ||
891 | { | 941 | { |
892 | struct sh_hdmi *hdmi = arg; | 942 | struct sh_hdmi *hdmi = arg; |
893 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 943 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
894 | 944 | ||
895 | pr_debug("%s(%p)\n", __func__, pdata->lcd_dev); | 945 | dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev); |
896 | /* PS mode e->a */ | 946 | /* PS mode e->a */ |
897 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); | 947 | hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); |
898 | } | 948 | } |
899 | 949 | ||
950 | static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) | ||
951 | { | ||
952 | struct fb_info *info = hdmi->info; | ||
953 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
954 | struct fb_var_screeninfo *new_var = &hdmi->var, *old_var = &ch->display_var; | ||
955 | struct fb_videomode mode1, mode2; | ||
956 | |||
957 | fb_var_to_videomode(&mode1, old_var); | ||
958 | fb_var_to_videomode(&mode2, new_var); | ||
959 | |||
960 | dev_dbg(info->dev, "Old %ux%u, new %ux%u\n", | ||
961 | mode1.xres, mode1.yres, mode2.xres, mode2.yres); | ||
962 | |||
963 | if (fb_mode_is_equal(&mode1, &mode2)) | ||
964 | return false; | ||
965 | |||
966 | dev_dbg(info->dev, "Switching %u -> %u lines\n", | ||
967 | mode1.yres, mode2.yres); | ||
968 | *old_var = *new_var; | ||
969 | |||
970 | return true; | ||
971 | } | ||
972 | |||
973 | /** | ||
974 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock | ||
975 | * @hdmi: driver context | ||
976 | * @pixclock: pixel clock period in picoseconds | ||
977 | * return: configured positive rate if successful | ||
978 | * 0 if couldn't set the rate, but managed to enable the clock | ||
979 | * negative error, if couldn't enable the clock | ||
980 | */ | ||
981 | static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) | ||
982 | { | ||
983 | long rate; | ||
984 | int ret; | ||
985 | |||
986 | rate = PICOS2KHZ(pixclock) * 1000; | ||
987 | rate = clk_round_rate(hdmi->hdmi_clk, rate); | ||
988 | if (rate > 0) { | ||
989 | ret = clk_set_rate(hdmi->hdmi_clk, rate); | ||
990 | if (ret < 0) { | ||
991 | dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret); | ||
992 | rate = 0; | ||
993 | } else { | ||
994 | dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate); | ||
995 | } | ||
996 | } else { | ||
997 | rate = 0; | ||
998 | dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate); | ||
999 | } | ||
1000 | |||
1001 | ret = clk_enable(hdmi->hdmi_clk); | ||
1002 | if (ret < 0) { | ||
1003 | dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); | ||
1004 | return ret; | ||
1005 | } | ||
1006 | |||
1007 | return rate; | ||
1008 | } | ||
1009 | |||
900 | /* Hotplug interrupt occurred, read EDID */ | 1010 | /* Hotplug interrupt occurred, read EDID */ |
901 | static void edid_work_fn(struct work_struct *work) | 1011 | static void sh_hdmi_edid_work_fn(struct work_struct *work) |
902 | { | 1012 | { |
903 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); | 1013 | struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); |
904 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; | 1014 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
1015 | struct sh_mobile_lcdc_chan *ch; | ||
1016 | int ret; | ||
905 | 1017 | ||
906 | pr_debug("%s(%p): begin, hotplug status %d\n", __func__, | 1018 | dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, |
907 | pdata->lcd_dev, hdmi->hp_state); | 1019 | pdata->lcd_dev, hdmi->hp_state); |
908 | 1020 | ||
909 | if (!pdata->lcd_dev) | 1021 | if (!pdata->lcd_dev) |
910 | return; | 1022 | return; |
911 | 1023 | ||
1024 | mutex_lock(&hdmi->mutex); | ||
1025 | |||
912 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { | 1026 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { |
913 | pm_runtime_get_sync(hdmi->dev); | ||
914 | /* A device has been plugged in */ | 1027 | /* A device has been plugged in */ |
915 | sh_hdmi_read_edid(hdmi); | 1028 | pm_runtime_get_sync(hdmi->dev); |
1029 | |||
1030 | ret = sh_hdmi_read_edid(hdmi); | ||
1031 | if (ret < 0) | ||
1032 | goto out; | ||
1033 | |||
1034 | /* Reconfigure the clock */ | ||
1035 | clk_disable(hdmi->hdmi_clk); | ||
1036 | ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock); | ||
1037 | if (ret < 0) | ||
1038 | goto out; | ||
1039 | |||
916 | msleep(10); | 1040 | msleep(10); |
917 | sh_hdmi_configure(hdmi); | 1041 | sh_hdmi_configure(hdmi); |
918 | /* Switched to another (d) power-save mode */ | 1042 | /* Switched to another (d) power-save mode */ |
919 | msleep(10); | 1043 | msleep(10); |
920 | 1044 | ||
921 | if (!hdmi->info) | 1045 | if (!hdmi->info) |
922 | return; | 1046 | goto out; |
1047 | |||
1048 | ch = hdmi->info->par; | ||
923 | 1049 | ||
924 | acquire_console_sem(); | 1050 | acquire_console_sem(); |
925 | 1051 | ||
926 | /* HDMI plug in */ | 1052 | /* HDMI plug in */ |
927 | hdmi->info->var = hdmi->var; | 1053 | if (!sh_hdmi_must_reconfigure(hdmi) && |
928 | if (hdmi->info->state != FBINFO_STATE_RUNNING) | 1054 | hdmi->info->state == FBINFO_STATE_RUNNING) { |
1055 | /* | ||
1056 | * First activation with the default monitor - just turn | ||
1057 | * on, if we run a resume here, the logo disappears | ||
1058 | */ | ||
1059 | if (lock_fb_info(hdmi->info)) { | ||
1060 | sh_hdmi_display_on(hdmi, hdmi->info); | ||
1061 | unlock_fb_info(hdmi->info); | ||
1062 | } | ||
1063 | } else { | ||
1064 | /* New monitor or have to wake up */ | ||
929 | fb_set_suspend(hdmi->info, 0); | 1065 | fb_set_suspend(hdmi->info, 0); |
930 | else | 1066 | } |
931 | hdmi_display_on(hdmi, hdmi->info); | ||
932 | 1067 | ||
933 | release_console_sem(); | 1068 | release_console_sem(); |
934 | } else { | 1069 | } else { |
1070 | ret = 0; | ||
935 | if (!hdmi->info) | 1071 | if (!hdmi->info) |
936 | return; | 1072 | goto out; |
937 | 1073 | ||
938 | acquire_console_sem(); | 1074 | acquire_console_sem(); |
939 | 1075 | ||
@@ -942,15 +1078,67 @@ static void edid_work_fn(struct work_struct *work) | |||
942 | 1078 | ||
943 | release_console_sem(); | 1079 | release_console_sem(); |
944 | pm_runtime_put(hdmi->dev); | 1080 | pm_runtime_put(hdmi->dev); |
1081 | fb_destroy_modedb(hdmi->monspec.modedb); | ||
945 | } | 1082 | } |
946 | 1083 | ||
947 | pr_debug("%s(%p): end\n", __func__, pdata->lcd_dev); | 1084 | out: |
1085 | if (ret < 0) | ||
1086 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
1087 | mutex_unlock(&hdmi->mutex); | ||
1088 | |||
1089 | dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev); | ||
1090 | } | ||
1091 | |||
1092 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
1093 | unsigned long action, void *data); | ||
1094 | |||
1095 | static struct notifier_block sh_hdmi_notifier = { | ||
1096 | .notifier_call = sh_hdmi_notify, | ||
1097 | }; | ||
1098 | |||
1099 | static int sh_hdmi_notify(struct notifier_block *nb, | ||
1100 | unsigned long action, void *data) | ||
1101 | { | ||
1102 | struct fb_event *event = data; | ||
1103 | struct fb_info *info = event->info; | ||
1104 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
1105 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; | ||
1106 | struct sh_hdmi *hdmi = board_cfg->board_data; | ||
1107 | |||
1108 | if (nb != &sh_hdmi_notifier || !hdmi || hdmi->info != info) | ||
1109 | return NOTIFY_DONE; | ||
1110 | |||
1111 | switch(action) { | ||
1112 | case FB_EVENT_FB_REGISTERED: | ||
1113 | /* Unneeded, activation taken care by sh_hdmi_display_on() */ | ||
1114 | break; | ||
1115 | case FB_EVENT_FB_UNREGISTERED: | ||
1116 | /* | ||
1117 | * We are called from unregister_framebuffer() with the | ||
1118 | * info->lock held. This is bad for us, because we can race with | ||
1119 | * the scheduled work, which has to call fb_set_suspend(), which | ||
1120 | * takes info->lock internally, so, sh_hdmi_edid_work_fn() | ||
1121 | * cannot take and hold info->lock for the whole function | ||
1122 | * duration. Using an additional lock creates a classical AB-BA | ||
1123 | * lock up. Therefore, we have to release the info->lock | ||
1124 | * temporarily, synchronise with the work queue and re-acquire | ||
1125 | * the info->lock. | ||
1126 | */ | ||
1127 | unlock_fb_info(hdmi->info); | ||
1128 | mutex_lock(&hdmi->mutex); | ||
1129 | hdmi->info = NULL; | ||
1130 | mutex_unlock(&hdmi->mutex); | ||
1131 | lock_fb_info(hdmi->info); | ||
1132 | return NOTIFY_OK; | ||
1133 | } | ||
1134 | return NOTIFY_DONE; | ||
948 | } | 1135 | } |
949 | 1136 | ||
950 | static int __init sh_hdmi_probe(struct platform_device *pdev) | 1137 | static int __init sh_hdmi_probe(struct platform_device *pdev) |
951 | { | 1138 | { |
952 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 1139 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
953 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1140 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1141 | struct sh_mobile_lcdc_board_cfg *board_cfg; | ||
954 | int irq = platform_get_irq(pdev, 0), ret; | 1142 | int irq = platform_get_irq(pdev, 0), ret; |
955 | struct sh_hdmi *hdmi; | 1143 | struct sh_hdmi *hdmi; |
956 | long rate; | 1144 | long rate; |
@@ -964,10 +1152,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
964 | return -ENOMEM; | 1152 | return -ENOMEM; |
965 | } | 1153 | } |
966 | 1154 | ||
967 | ret = snd_soc_register_codec(&pdev->dev, | 1155 | mutex_init(&hdmi->mutex); |
968 | &soc_codec_dev_sh_hdmi, &sh_hdmi_dai, 1); | ||
969 | if (ret < 0) | ||
970 | goto esndreg; | ||
971 | 1156 | ||
972 | hdmi->dev = &pdev->dev; | 1157 | hdmi->dev = &pdev->dev; |
973 | 1158 | ||
@@ -978,30 +1163,14 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
978 | goto egetclk; | 1163 | goto egetclk; |
979 | } | 1164 | } |
980 | 1165 | ||
981 | rate = PICOS2KHZ(pdata->lcd_chan->lcd_cfg.pixclock) * 1000; | 1166 | /* Some arbitrary relaxed pixclock just to get things started */ |
982 | 1167 | rate = sh_hdmi_clk_configure(hdmi, 37037); | |
983 | rate = clk_round_rate(hdmi->hdmi_clk, rate); | ||
984 | if (rate < 0) { | 1168 | if (rate < 0) { |
985 | ret = rate; | 1169 | ret = rate; |
986 | dev_err(&pdev->dev, "Cannot get suitable rate: %ld\n", rate); | ||
987 | goto erate; | 1170 | goto erate; |
988 | } | 1171 | } |
989 | 1172 | ||
990 | ret = clk_set_rate(hdmi->hdmi_clk, rate); | 1173 | dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); |
991 | if (ret < 0) { | ||
992 | dev_err(&pdev->dev, "Cannot set rate %ld: %d\n", rate, ret); | ||
993 | goto erate; | ||
994 | } | ||
995 | |||
996 | pr_debug("HDMI set frequency %lu\n", rate); | ||
997 | |||
998 | ret = clk_enable(hdmi->hdmi_clk); | ||
999 | if (ret < 0) { | ||
1000 | dev_err(&pdev->dev, "Cannot enable clock: %d\n", ret); | ||
1001 | goto eclkenable; | ||
1002 | } | ||
1003 | |||
1004 | dev_info(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); | ||
1005 | 1174 | ||
1006 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { | 1175 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { |
1007 | dev_err(&pdev->dev, "HDMI register region already claimed\n"); | 1176 | dev_err(&pdev->dev, "HDMI register region already claimed\n"); |
@@ -1018,18 +1187,18 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1018 | 1187 | ||
1019 | platform_set_drvdata(pdev, hdmi); | 1188 | platform_set_drvdata(pdev, hdmi); |
1020 | 1189 | ||
1021 | #if 1 | ||
1022 | /* Product and revision IDs are 0 in sh-mobile version */ | 1190 | /* Product and revision IDs are 0 in sh-mobile version */ |
1023 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | 1191 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", |
1024 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | 1192 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); |
1025 | #endif | ||
1026 | 1193 | ||
1027 | /* Set up LCDC callbacks */ | 1194 | /* Set up LCDC callbacks */ |
1028 | pdata->lcd_chan->board_cfg.board_data = hdmi; | 1195 | board_cfg = &pdata->lcd_chan->board_cfg; |
1029 | pdata->lcd_chan->board_cfg.display_on = hdmi_display_on; | 1196 | board_cfg->owner = THIS_MODULE; |
1030 | pdata->lcd_chan->board_cfg.display_off = hdmi_display_off; | 1197 | board_cfg->board_data = hdmi; |
1198 | board_cfg->display_on = sh_hdmi_display_on; | ||
1199 | board_cfg->display_off = sh_hdmi_display_off; | ||
1031 | 1200 | ||
1032 | INIT_DELAYED_WORK(&hdmi->edid_work, edid_work_fn); | 1201 | INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); |
1033 | 1202 | ||
1034 | pm_runtime_enable(&pdev->dev); | 1203 | pm_runtime_enable(&pdev->dev); |
1035 | pm_runtime_resume(&pdev->dev); | 1204 | pm_runtime_resume(&pdev->dev); |
@@ -1041,8 +1210,17 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1041 | goto ereqirq; | 1210 | goto ereqirq; |
1042 | } | 1211 | } |
1043 | 1212 | ||
1213 | ret = snd_soc_register_codec(&pdev->dev, | ||
1214 | &soc_codec_dev_sh_hdmi, &sh_hdmi_dai, 1); | ||
1215 | if (ret < 0) { | ||
1216 | dev_err(&pdev->dev, "codec registration failed\n"); | ||
1217 | goto ecodec; | ||
1218 | } | ||
1219 | |||
1044 | return 0; | 1220 | return 0; |
1045 | 1221 | ||
1222 | ecodec: | ||
1223 | free_irq(irq, hdmi); | ||
1046 | ereqirq: | 1224 | ereqirq: |
1047 | pm_runtime_disable(&pdev->dev); | 1225 | pm_runtime_disable(&pdev->dev); |
1048 | iounmap(hdmi->base); | 1226 | iounmap(hdmi->base); |
@@ -1050,12 +1228,10 @@ emap: | |||
1050 | release_mem_region(res->start, resource_size(res)); | 1228 | release_mem_region(res->start, resource_size(res)); |
1051 | ereqreg: | 1229 | ereqreg: |
1052 | clk_disable(hdmi->hdmi_clk); | 1230 | clk_disable(hdmi->hdmi_clk); |
1053 | eclkenable: | ||
1054 | erate: | 1231 | erate: |
1055 | clk_put(hdmi->hdmi_clk); | 1232 | clk_put(hdmi->hdmi_clk); |
1056 | egetclk: | 1233 | egetclk: |
1057 | snd_soc_unregister_codec(&pdev->dev); | 1234 | mutex_destroy(&hdmi->mutex); |
1058 | esndreg: | ||
1059 | kfree(hdmi); | 1235 | kfree(hdmi); |
1060 | 1236 | ||
1061 | return ret; | 1237 | return ret; |
@@ -1066,21 +1242,26 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev) | |||
1066 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; | 1242 | struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; |
1067 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); | 1243 | struct sh_hdmi *hdmi = platform_get_drvdata(pdev); |
1068 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1244 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1245 | struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg; | ||
1069 | int irq = platform_get_irq(pdev, 0); | 1246 | int irq = platform_get_irq(pdev, 0); |
1070 | 1247 | ||
1071 | snd_soc_unregister_codec(&pdev->dev); | 1248 | snd_soc_unregister_codec(&pdev->dev); |
1072 | 1249 | ||
1073 | pdata->lcd_chan->board_cfg.display_on = NULL; | 1250 | board_cfg->display_on = NULL; |
1074 | pdata->lcd_chan->board_cfg.display_off = NULL; | 1251 | board_cfg->display_off = NULL; |
1075 | pdata->lcd_chan->board_cfg.board_data = NULL; | 1252 | board_cfg->board_data = NULL; |
1253 | board_cfg->owner = NULL; | ||
1076 | 1254 | ||
1255 | /* No new work will be scheduled, wait for running ISR */ | ||
1077 | free_irq(irq, hdmi); | 1256 | free_irq(irq, hdmi); |
1078 | pm_runtime_disable(&pdev->dev); | 1257 | /* Wait for already scheduled work */ |
1079 | cancel_delayed_work_sync(&hdmi->edid_work); | 1258 | cancel_delayed_work_sync(&hdmi->edid_work); |
1259 | pm_runtime_disable(&pdev->dev); | ||
1080 | clk_disable(hdmi->hdmi_clk); | 1260 | clk_disable(hdmi->hdmi_clk); |
1081 | clk_put(hdmi->hdmi_clk); | 1261 | clk_put(hdmi->hdmi_clk); |
1082 | iounmap(hdmi->base); | 1262 | iounmap(hdmi->base); |
1083 | release_mem_region(res->start, resource_size(res)); | 1263 | release_mem_region(res->start, resource_size(res)); |
1264 | mutex_destroy(&hdmi->mutex); | ||
1084 | kfree(hdmi); | 1265 | kfree(hdmi); |
1085 | 1266 | ||
1086 | return 0; | 1267 | return 0; |
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 7a1419279c8f..50963739a409 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
@@ -12,7 +12,6 @@ | |||
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
14 | #include <linux/mm.h> | 14 | #include <linux/mm.h> |
15 | #include <linux/fb.h> | ||
16 | #include <linux/clk.h> | 15 | #include <linux/clk.h> |
17 | #include <linux/pm_runtime.h> | 16 | #include <linux/pm_runtime.h> |
18 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
@@ -21,10 +20,12 @@ | |||
21 | #include <linux/vmalloc.h> | 20 | #include <linux/vmalloc.h> |
22 | #include <linux/ioctl.h> | 21 | #include <linux/ioctl.h> |
23 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | #include <linux/console.h> | ||
24 | #include <video/sh_mobile_lcdc.h> | 24 | #include <video/sh_mobile_lcdc.h> |
25 | #include <asm/atomic.h> | 25 | #include <asm/atomic.h> |
26 | 26 | ||
27 | #define PALETTE_NR 16 | 27 | #include "sh_mobile_lcdcfb.h" |
28 | |||
28 | #define SIDE_B_OFFSET 0x1000 | 29 | #define SIDE_B_OFFSET 0x1000 |
29 | #define MIRROR_OFFSET 0x2000 | 30 | #define MIRROR_OFFSET 0x2000 |
30 | 31 | ||
@@ -53,11 +54,8 @@ static int lcdc_shared_regs[] = { | |||
53 | }; | 54 | }; |
54 | #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) | 55 | #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) |
55 | 56 | ||
56 | /* per-channel registers */ | 57 | #define DEFAULT_XRES 1280 |
57 | enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, | 58 | #define DEFAULT_YRES 1024 |
58 | LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, | ||
59 | LDHAJR, | ||
60 | NR_CH_REGS }; | ||
61 | 59 | ||
62 | static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { | 60 | static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { |
63 | [LDDCKPAT1R] = 0x400, | 61 | [LDDCKPAT1R] = 0x400, |
@@ -112,23 +110,21 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { | |||
112 | #define LDRCNTR_MRC 0x00000001 | 110 | #define LDRCNTR_MRC 0x00000001 |
113 | #define LDSR_MRS 0x00000100 | 111 | #define LDSR_MRS 0x00000100 |
114 | 112 | ||
115 | struct sh_mobile_lcdc_priv; | 113 | static const struct fb_videomode default_720p = { |
116 | struct sh_mobile_lcdc_chan { | 114 | .name = "HDMI 720p", |
117 | struct sh_mobile_lcdc_priv *lcdc; | 115 | .xres = 1280, |
118 | unsigned long *reg_offs; | 116 | .yres = 720, |
119 | unsigned long ldmt1r_value; | 117 | |
120 | unsigned long enabled; /* ME and SE in LDCNT2R */ | 118 | .left_margin = 200, |
121 | struct sh_mobile_lcdc_chan_cfg cfg; | 119 | .right_margin = 88, |
122 | u32 pseudo_palette[PALETTE_NR]; | 120 | .hsync_len = 48, |
123 | unsigned long saved_ch_regs[NR_CH_REGS]; | 121 | |
124 | struct fb_info *info; | 122 | .upper_margin = 20, |
125 | dma_addr_t dma_handle; | 123 | .lower_margin = 5, |
126 | struct fb_deferred_io defio; | 124 | .vsync_len = 5, |
127 | struct scatterlist *sglist; | 125 | |
128 | unsigned long frame_end; | 126 | .pixclock = 13468, |
129 | unsigned long pan_offset; | 127 | .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, |
130 | wait_queue_head_t frame_end_wait; | ||
131 | struct completion vsync_completion; | ||
132 | }; | 128 | }; |
133 | 129 | ||
134 | struct sh_mobile_lcdc_priv { | 130 | struct sh_mobile_lcdc_priv { |
@@ -409,8 +405,8 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, | |||
409 | 405 | ||
410 | static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) | 406 | static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) |
411 | { | 407 | { |
412 | struct fb_var_screeninfo *var = &ch->info->var; | 408 | struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var; |
413 | unsigned long h_total, hsync_pos; | 409 | unsigned long h_total, hsync_pos, display_h_total; |
414 | u32 tmp; | 410 | u32 tmp; |
415 | 411 | ||
416 | tmp = ch->ldmt1r_value; | 412 | tmp = ch->ldmt1r_value; |
@@ -428,31 +424,33 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) | |||
428 | lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); | 424 | lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); |
429 | 425 | ||
430 | /* horizontal configuration */ | 426 | /* horizontal configuration */ |
431 | h_total = var->xres + var->hsync_len + | 427 | h_total = display_var->xres + display_var->hsync_len + |
432 | var->left_margin + var->right_margin; | 428 | display_var->left_margin + display_var->right_margin; |
433 | tmp = h_total / 8; /* HTCN */ | 429 | tmp = h_total / 8; /* HTCN */ |
434 | tmp |= (var->xres / 8) << 16; /* HDCN */ | 430 | tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */ |
435 | lcdc_write_chan(ch, LDHCNR, tmp); | 431 | lcdc_write_chan(ch, LDHCNR, tmp); |
436 | 432 | ||
437 | hsync_pos = var->xres + var->right_margin; | 433 | hsync_pos = display_var->xres + display_var->right_margin; |
438 | tmp = hsync_pos / 8; /* HSYNP */ | 434 | tmp = hsync_pos / 8; /* HSYNP */ |
439 | tmp |= (var->hsync_len / 8) << 16; /* HSYNW */ | 435 | tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */ |
440 | lcdc_write_chan(ch, LDHSYNR, tmp); | 436 | lcdc_write_chan(ch, LDHSYNR, tmp); |
441 | 437 | ||
442 | /* vertical configuration */ | 438 | /* vertical configuration */ |
443 | tmp = var->yres + var->vsync_len + | 439 | tmp = display_var->yres + display_var->vsync_len + |
444 | var->upper_margin + var->lower_margin; /* VTLN */ | 440 | display_var->upper_margin + display_var->lower_margin; /* VTLN */ |
445 | tmp |= var->yres << 16; /* VDLN */ | 441 | tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */ |
446 | lcdc_write_chan(ch, LDVLNR, tmp); | 442 | lcdc_write_chan(ch, LDVLNR, tmp); |
447 | 443 | ||
448 | tmp = var->yres + var->lower_margin; /* VSYNP */ | 444 | tmp = display_var->yres + display_var->lower_margin; /* VSYNP */ |
449 | tmp |= var->vsync_len << 16; /* VSYNW */ | 445 | tmp |= display_var->vsync_len << 16; /* VSYNW */ |
450 | lcdc_write_chan(ch, LDVSYNR, tmp); | 446 | lcdc_write_chan(ch, LDVSYNR, tmp); |
451 | 447 | ||
452 | /* Adjust horizontal synchronisation for HDMI */ | 448 | /* Adjust horizontal synchronisation for HDMI */ |
453 | tmp = ((var->xres & 7) << 24) | | 449 | display_h_total = display_var->xres + display_var->hsync_len + |
454 | ((h_total & 7) << 16) | | 450 | display_var->left_margin + display_var->right_margin; |
455 | ((var->hsync_len & 7) << 8) | | 451 | tmp = ((display_var->xres & 7) << 24) | |
452 | ((display_h_total & 7) << 16) | | ||
453 | ((display_var->hsync_len & 7) << 8) | | ||
456 | hsync_pos; | 454 | hsync_pos; |
457 | lcdc_write_chan(ch, LDHAJR, tmp); | 455 | lcdc_write_chan(ch, LDHAJR, tmp); |
458 | } | 456 | } |
@@ -460,7 +458,6 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) | |||
460 | static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | 458 | static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) |
461 | { | 459 | { |
462 | struct sh_mobile_lcdc_chan *ch; | 460 | struct sh_mobile_lcdc_chan *ch; |
463 | struct fb_videomode *lcd_cfg; | ||
464 | struct sh_mobile_lcdc_board_cfg *board_cfg; | 461 | struct sh_mobile_lcdc_board_cfg *board_cfg; |
465 | unsigned long tmp; | 462 | unsigned long tmp; |
466 | int k, m; | 463 | int k, m; |
@@ -503,7 +500,8 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
503 | m = 1 << 6; | 500 | m = 1 << 6; |
504 | tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); | 501 | tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); |
505 | 502 | ||
506 | lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000); | 503 | /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */ |
504 | lcdc_write_chan(ch, LDDCKPAT1R, 0); | ||
507 | lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); | 505 | lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); |
508 | } | 506 | } |
509 | 507 | ||
@@ -518,7 +516,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
518 | 516 | ||
519 | for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { | 517 | for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { |
520 | ch = &priv->ch[k]; | 518 | ch = &priv->ch[k]; |
521 | lcd_cfg = &ch->cfg.lcd_cfg; | ||
522 | 519 | ||
523 | if (!ch->enabled) | 520 | if (!ch->enabled) |
524 | continue; | 521 | continue; |
@@ -547,7 +544,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
547 | 544 | ||
548 | /* set bpp format in PKF[4:0] */ | 545 | /* set bpp format in PKF[4:0] */ |
549 | tmp = lcdc_read_chan(ch, LDDFR); | 546 | tmp = lcdc_read_chan(ch, LDDFR); |
550 | tmp &= ~(0x0001001f); | 547 | tmp &= ~0x0001001f; |
551 | tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; | 548 | tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; |
552 | lcdc_write_chan(ch, LDDFR, tmp); | 549 | lcdc_write_chan(ch, LDDFR, tmp); |
553 | 550 | ||
@@ -591,8 +588,10 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) | |||
591 | continue; | 588 | continue; |
592 | 589 | ||
593 | board_cfg = &ch->cfg.board_cfg; | 590 | board_cfg = &ch->cfg.board_cfg; |
594 | if (board_cfg->display_on) | 591 | if (try_module_get(board_cfg->owner) && board_cfg->display_on) { |
595 | board_cfg->display_on(board_cfg->board_data, ch->info); | 592 | board_cfg->display_on(board_cfg->board_data, ch->info); |
593 | module_put(board_cfg->owner); | ||
594 | } | ||
596 | } | 595 | } |
597 | 596 | ||
598 | return 0; | 597 | return 0; |
@@ -614,7 +613,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) | |||
614 | * flush frame, and wait for frame end interrupt | 613 | * flush frame, and wait for frame end interrupt |
615 | * clean up deferred io and enable clock | 614 | * clean up deferred io and enable clock |
616 | */ | 615 | */ |
617 | if (ch->info->fbdefio) { | 616 | if (ch->info && ch->info->fbdefio) { |
618 | ch->frame_end = 0; | 617 | ch->frame_end = 0; |
619 | schedule_delayed_work(&ch->info->deferred_work, 0); | 618 | schedule_delayed_work(&ch->info->deferred_work, 0); |
620 | wait_event(ch->frame_end_wait, ch->frame_end); | 619 | wait_event(ch->frame_end_wait, ch->frame_end); |
@@ -624,8 +623,10 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) | |||
624 | } | 623 | } |
625 | 624 | ||
626 | board_cfg = &ch->cfg.board_cfg; | 625 | board_cfg = &ch->cfg.board_cfg; |
627 | if (board_cfg->display_off) | 626 | if (try_module_get(board_cfg->owner) && board_cfg->display_off) { |
628 | board_cfg->display_off(board_cfg->board_data); | 627 | board_cfg->display_off(board_cfg->board_data); |
628 | module_put(board_cfg->owner); | ||
629 | } | ||
629 | } | 630 | } |
630 | 631 | ||
631 | /* stop the lcdc */ | 632 | /* stop the lcdc */ |
@@ -704,7 +705,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, | |||
704 | return PTR_ERR(priv->dot_clk); | 705 | return PTR_ERR(priv->dot_clk); |
705 | } | 706 | } |
706 | } | 707 | } |
707 | atomic_set(&priv->hw_usecnt, -1); | ||
708 | 708 | ||
709 | /* Runtime PM support involves two step for this driver: | 709 | /* Runtime PM support involves two step for this driver: |
710 | * 1) Enable Runtime PM | 710 | * 1) Enable Runtime PM |
@@ -837,6 +837,102 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, | |||
837 | return retval; | 837 | return retval; |
838 | } | 838 | } |
839 | 839 | ||
840 | static void sh_mobile_fb_reconfig(struct fb_info *info) | ||
841 | { | ||
842 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
843 | struct fb_videomode mode1, mode2; | ||
844 | struct fb_event event; | ||
845 | int evnt = FB_EVENT_MODE_CHANGE_ALL; | ||
846 | |||
847 | if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par)) | ||
848 | /* More framebuffer users are active */ | ||
849 | return; | ||
850 | |||
851 | fb_var_to_videomode(&mode1, &ch->display_var); | ||
852 | fb_var_to_videomode(&mode2, &info->var); | ||
853 | |||
854 | if (fb_mode_is_equal(&mode1, &mode2)) | ||
855 | return; | ||
856 | |||
857 | /* Display has been re-plugged, framebuffer is free now, reconfigure */ | ||
858 | if (fb_set_var(info, &ch->display_var) < 0) | ||
859 | /* Couldn't reconfigure, hopefully, can continue as before */ | ||
860 | return; | ||
861 | |||
862 | info->fix.line_length = mode2.xres * (ch->cfg.bpp / 8); | ||
863 | |||
864 | /* | ||
865 | * fb_set_var() calls the notifier change internally, only if | ||
866 | * FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a | ||
867 | * user event, we have to call the chain ourselves. | ||
868 | */ | ||
869 | event.info = info; | ||
870 | event.data = &mode2; | ||
871 | fb_notifier_call_chain(evnt, &event); | ||
872 | } | ||
873 | |||
874 | /* | ||
875 | * Locking: both .fb_release() and .fb_open() are called with info->lock held if | ||
876 | * user == 1, or with console sem held, if user == 0. | ||
877 | */ | ||
878 | static int sh_mobile_release(struct fb_info *info, int user) | ||
879 | { | ||
880 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
881 | |||
882 | mutex_lock(&ch->open_lock); | ||
883 | dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count); | ||
884 | |||
885 | ch->use_count--; | ||
886 | |||
887 | /* Nothing to reconfigure, when called from fbcon */ | ||
888 | if (user) { | ||
889 | acquire_console_sem(); | ||
890 | sh_mobile_fb_reconfig(info); | ||
891 | release_console_sem(); | ||
892 | } | ||
893 | |||
894 | mutex_unlock(&ch->open_lock); | ||
895 | |||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | static int sh_mobile_open(struct fb_info *info, int user) | ||
900 | { | ||
901 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
902 | |||
903 | mutex_lock(&ch->open_lock); | ||
904 | ch->use_count++; | ||
905 | |||
906 | dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count); | ||
907 | mutex_unlock(&ch->open_lock); | ||
908 | |||
909 | return 0; | ||
910 | } | ||
911 | |||
912 | static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
913 | { | ||
914 | struct sh_mobile_lcdc_chan *ch = info->par; | ||
915 | |||
916 | if (var->xres < 160 || var->xres > 1920 || | ||
917 | var->yres < 120 || var->yres > 1080 || | ||
918 | var->left_margin < 32 || var->left_margin > 320 || | ||
919 | var->right_margin < 12 || var->right_margin > 240 || | ||
920 | var->upper_margin < 12 || var->upper_margin > 120 || | ||
921 | var->lower_margin < 1 || var->lower_margin > 64 || | ||
922 | var->hsync_len < 32 || var->hsync_len > 240 || | ||
923 | var->vsync_len < 2 || var->vsync_len > 64 || | ||
924 | var->pixclock < 6000 || var->pixclock > 40000 || | ||
925 | var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) { | ||
926 | dev_warn(info->dev, "Invalid info: %u %u %u %u %u %u %u %u %u!\n", | ||
927 | var->xres, var->yres, | ||
928 | var->left_margin, var->right_margin, | ||
929 | var->upper_margin, var->lower_margin, | ||
930 | var->hsync_len, var->vsync_len, | ||
931 | var->pixclock); | ||
932 | return -EINVAL; | ||
933 | } | ||
934 | return 0; | ||
935 | } | ||
840 | 936 | ||
841 | static struct fb_ops sh_mobile_lcdc_ops = { | 937 | static struct fb_ops sh_mobile_lcdc_ops = { |
842 | .owner = THIS_MODULE, | 938 | .owner = THIS_MODULE, |
@@ -848,6 +944,9 @@ static struct fb_ops sh_mobile_lcdc_ops = { | |||
848 | .fb_imageblit = sh_mobile_lcdc_imageblit, | 944 | .fb_imageblit = sh_mobile_lcdc_imageblit, |
849 | .fb_pan_display = sh_mobile_fb_pan_display, | 945 | .fb_pan_display = sh_mobile_fb_pan_display, |
850 | .fb_ioctl = sh_mobile_ioctl, | 946 | .fb_ioctl = sh_mobile_ioctl, |
947 | .fb_open = sh_mobile_open, | ||
948 | .fb_release = sh_mobile_release, | ||
949 | .fb_check_var = sh_mobile_check_var, | ||
851 | }; | 950 | }; |
852 | 951 | ||
853 | static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) | 952 | static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) |
@@ -958,6 +1057,7 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { | |||
958 | .runtime_resume = sh_mobile_lcdc_runtime_resume, | 1057 | .runtime_resume = sh_mobile_lcdc_runtime_resume, |
959 | }; | 1058 | }; |
960 | 1059 | ||
1060 | /* locking: called with info->lock held */ | ||
961 | static int sh_mobile_lcdc_notify(struct notifier_block *nb, | 1061 | static int sh_mobile_lcdc_notify(struct notifier_block *nb, |
962 | unsigned long action, void *data) | 1062 | unsigned long action, void *data) |
963 | { | 1063 | { |
@@ -965,53 +1065,40 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb, | |||
965 | struct fb_info *info = event->info; | 1065 | struct fb_info *info = event->info; |
966 | struct sh_mobile_lcdc_chan *ch = info->par; | 1066 | struct sh_mobile_lcdc_chan *ch = info->par; |
967 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; | 1067 | struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; |
968 | struct fb_var_screeninfo *var; | 1068 | int ret; |
969 | 1069 | ||
970 | if (&ch->lcdc->notifier != nb) | 1070 | if (&ch->lcdc->notifier != nb) |
971 | return 0; | 1071 | return NOTIFY_DONE; |
972 | 1072 | ||
973 | dev_dbg(info->dev, "%s(): action = %lu, data = %p\n", | 1073 | dev_dbg(info->dev, "%s(): action = %lu, data = %p\n", |
974 | __func__, action, event->data); | 1074 | __func__, action, event->data); |
975 | 1075 | ||
976 | switch(action) { | 1076 | switch(action) { |
977 | case FB_EVENT_SUSPEND: | 1077 | case FB_EVENT_SUSPEND: |
978 | if (board_cfg->display_off) | 1078 | if (try_module_get(board_cfg->owner) && board_cfg->display_off) { |
979 | board_cfg->display_off(board_cfg->board_data); | 1079 | board_cfg->display_off(board_cfg->board_data); |
1080 | module_put(board_cfg->owner); | ||
1081 | } | ||
980 | pm_runtime_put(info->device); | 1082 | pm_runtime_put(info->device); |
1083 | sh_mobile_lcdc_stop(ch->lcdc); | ||
981 | break; | 1084 | break; |
982 | case FB_EVENT_RESUME: | 1085 | case FB_EVENT_RESUME: |
983 | var = &info->var; | 1086 | mutex_lock(&ch->open_lock); |
1087 | sh_mobile_fb_reconfig(info); | ||
1088 | mutex_unlock(&ch->open_lock); | ||
984 | 1089 | ||
985 | /* HDMI must be enabled before LCDC configuration */ | 1090 | /* HDMI must be enabled before LCDC configuration */ |
986 | if (board_cfg->display_on) | 1091 | if (try_module_get(board_cfg->owner) && board_cfg->display_on) { |
987 | board_cfg->display_on(board_cfg->board_data, ch->info); | 1092 | board_cfg->display_on(board_cfg->board_data, info); |
988 | 1093 | module_put(board_cfg->owner); | |
989 | /* Check if the new display is not in our modelist */ | ||
990 | if (ch->info->modelist.next && | ||
991 | !fb_match_mode(var, &ch->info->modelist)) { | ||
992 | struct fb_videomode mode; | ||
993 | int ret; | ||
994 | |||
995 | /* Can we handle this display? */ | ||
996 | if (var->xres > ch->cfg.lcd_cfg.xres || | ||
997 | var->yres > ch->cfg.lcd_cfg.yres) | ||
998 | return -ENOMEM; | ||
999 | |||
1000 | /* Add to the modelist */ | ||
1001 | fb_var_to_videomode(&mode, var); | ||
1002 | ret = fb_add_videomode(&mode, &ch->info->modelist); | ||
1003 | if (ret < 0) | ||
1004 | return ret; | ||
1005 | } | 1094 | } |
1006 | 1095 | ||
1007 | pm_runtime_get_sync(info->device); | 1096 | ret = sh_mobile_lcdc_start(ch->lcdc); |
1008 | 1097 | if (!ret) | |
1009 | sh_mobile_lcdc_geometry(ch); | 1098 | pm_runtime_get_sync(info->device); |
1010 | |||
1011 | break; | ||
1012 | } | 1099 | } |
1013 | 1100 | ||
1014 | return 0; | 1101 | return NOTIFY_OK; |
1015 | } | 1102 | } |
1016 | 1103 | ||
1017 | static int sh_mobile_lcdc_remove(struct platform_device *pdev); | 1104 | static int sh_mobile_lcdc_remove(struct platform_device *pdev); |
@@ -1020,14 +1107,13 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
1020 | { | 1107 | { |
1021 | struct fb_info *info; | 1108 | struct fb_info *info; |
1022 | struct sh_mobile_lcdc_priv *priv; | 1109 | struct sh_mobile_lcdc_priv *priv; |
1023 | struct sh_mobile_lcdc_info *pdata; | 1110 | struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data; |
1024 | struct sh_mobile_lcdc_chan_cfg *cfg; | ||
1025 | struct resource *res; | 1111 | struct resource *res; |
1026 | int error; | 1112 | int error; |
1027 | void *buf; | 1113 | void *buf; |
1028 | int i, j; | 1114 | int i, j; |
1029 | 1115 | ||
1030 | if (!pdev->dev.platform_data) { | 1116 | if (!pdata) { |
1031 | dev_err(&pdev->dev, "no platform data defined\n"); | 1117 | dev_err(&pdev->dev, "no platform data defined\n"); |
1032 | return -EINVAL; | 1118 | return -EINVAL; |
1033 | } | 1119 | } |
@@ -1055,31 +1141,33 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
1055 | } | 1141 | } |
1056 | 1142 | ||
1057 | priv->irq = i; | 1143 | priv->irq = i; |
1058 | pdata = pdev->dev.platform_data; | 1144 | atomic_set(&priv->hw_usecnt, -1); |
1059 | 1145 | ||
1060 | j = 0; | 1146 | j = 0; |
1061 | for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) { | 1147 | for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) { |
1062 | priv->ch[j].lcdc = priv; | 1148 | struct sh_mobile_lcdc_chan *ch = priv->ch + j; |
1063 | memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i])); | 1149 | |
1150 | ch->lcdc = priv; | ||
1151 | memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i])); | ||
1064 | 1152 | ||
1065 | error = sh_mobile_lcdc_check_interface(&priv->ch[j]); | 1153 | error = sh_mobile_lcdc_check_interface(ch); |
1066 | if (error) { | 1154 | if (error) { |
1067 | dev_err(&pdev->dev, "unsupported interface type\n"); | 1155 | dev_err(&pdev->dev, "unsupported interface type\n"); |
1068 | goto err1; | 1156 | goto err1; |
1069 | } | 1157 | } |
1070 | init_waitqueue_head(&priv->ch[j].frame_end_wait); | 1158 | init_waitqueue_head(&ch->frame_end_wait); |
1071 | init_completion(&priv->ch[j].vsync_completion); | 1159 | init_completion(&ch->vsync_completion); |
1072 | priv->ch[j].pan_offset = 0; | 1160 | ch->pan_offset = 0; |
1073 | 1161 | ||
1074 | switch (pdata->ch[i].chan) { | 1162 | switch (pdata->ch[i].chan) { |
1075 | case LCDC_CHAN_MAINLCD: | 1163 | case LCDC_CHAN_MAINLCD: |
1076 | priv->ch[j].enabled = 1 << 1; | 1164 | ch->enabled = 1 << 1; |
1077 | priv->ch[j].reg_offs = lcdc_offs_mainlcd; | 1165 | ch->reg_offs = lcdc_offs_mainlcd; |
1078 | j++; | 1166 | j++; |
1079 | break; | 1167 | break; |
1080 | case LCDC_CHAN_SUBLCD: | 1168 | case LCDC_CHAN_SUBLCD: |
1081 | priv->ch[j].enabled = 1 << 2; | 1169 | ch->enabled = 1 << 2; |
1082 | priv->ch[j].reg_offs = lcdc_offs_sublcd; | 1170 | ch->reg_offs = lcdc_offs_sublcd; |
1083 | j++; | 1171 | j++; |
1084 | break; | 1172 | break; |
1085 | } | 1173 | } |
@@ -1103,69 +1191,83 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
1103 | 1191 | ||
1104 | for (i = 0; i < j; i++) { | 1192 | for (i = 0; i < j; i++) { |
1105 | struct fb_var_screeninfo *var; | 1193 | struct fb_var_screeninfo *var; |
1106 | struct fb_videomode *lcd_cfg; | 1194 | const struct fb_videomode *lcd_cfg, *max_cfg = NULL; |
1107 | cfg = &priv->ch[i].cfg; | 1195 | struct sh_mobile_lcdc_chan *ch = priv->ch + i; |
1196 | struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg; | ||
1197 | const struct fb_videomode *mode = cfg->lcd_cfg; | ||
1198 | unsigned long max_size = 0; | ||
1199 | int k; | ||
1108 | 1200 | ||
1109 | priv->ch[i].info = framebuffer_alloc(0, &pdev->dev); | 1201 | ch->info = framebuffer_alloc(0, &pdev->dev); |
1110 | if (!priv->ch[i].info) { | 1202 | if (!ch->info) { |
1111 | dev_err(&pdev->dev, "unable to allocate fb_info\n"); | 1203 | dev_err(&pdev->dev, "unable to allocate fb_info\n"); |
1112 | error = -ENOMEM; | 1204 | error = -ENOMEM; |
1113 | break; | 1205 | break; |
1114 | } | 1206 | } |
1115 | 1207 | ||
1116 | info = priv->ch[i].info; | 1208 | info = ch->info; |
1117 | var = &info->var; | 1209 | var = &info->var; |
1118 | lcd_cfg = &cfg->lcd_cfg; | ||
1119 | info->fbops = &sh_mobile_lcdc_ops; | 1210 | info->fbops = &sh_mobile_lcdc_ops; |
1120 | var->xres = var->xres_virtual = lcd_cfg->xres; | 1211 | info->par = ch; |
1121 | var->yres = lcd_cfg->yres; | 1212 | |
1213 | mutex_init(&ch->open_lock); | ||
1214 | |||
1215 | for (k = 0, lcd_cfg = mode; | ||
1216 | k < cfg->num_cfg && lcd_cfg; | ||
1217 | k++, lcd_cfg++) { | ||
1218 | unsigned long size = lcd_cfg->yres * lcd_cfg->xres; | ||
1219 | |||
1220 | if (size > max_size) { | ||
1221 | max_cfg = lcd_cfg; | ||
1222 | max_size = size; | ||
1223 | } | ||
1224 | } | ||
1225 | |||
1226 | if (!mode) | ||
1227 | max_size = DEFAULT_XRES * DEFAULT_YRES; | ||
1228 | else if (max_cfg) | ||
1229 | dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n", | ||
1230 | max_cfg->xres, max_cfg->yres); | ||
1231 | |||
1232 | info->fix = sh_mobile_lcdc_fix; | ||
1233 | info->fix.smem_len = max_size * (cfg->bpp / 8) * 2; | ||
1234 | |||
1235 | if (!mode) | ||
1236 | mode = &default_720p; | ||
1237 | |||
1238 | fb_videomode_to_var(var, mode); | ||
1122 | /* Default Y virtual resolution is 2x panel size */ | 1239 | /* Default Y virtual resolution is 2x panel size */ |
1123 | var->yres_virtual = var->yres * 2; | 1240 | var->yres_virtual = var->yres * 2; |
1124 | var->width = cfg->lcd_size_cfg.width; | ||
1125 | var->height = cfg->lcd_size_cfg.height; | ||
1126 | var->activate = FB_ACTIVATE_NOW; | 1241 | var->activate = FB_ACTIVATE_NOW; |
1127 | var->left_margin = lcd_cfg->left_margin; | ||
1128 | var->right_margin = lcd_cfg->right_margin; | ||
1129 | var->upper_margin = lcd_cfg->upper_margin; | ||
1130 | var->lower_margin = lcd_cfg->lower_margin; | ||
1131 | var->hsync_len = lcd_cfg->hsync_len; | ||
1132 | var->vsync_len = lcd_cfg->vsync_len; | ||
1133 | var->sync = lcd_cfg->sync; | ||
1134 | var->pixclock = lcd_cfg->pixclock; | ||
1135 | 1242 | ||
1136 | error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); | 1243 | error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); |
1137 | if (error) | 1244 | if (error) |
1138 | break; | 1245 | break; |
1139 | 1246 | ||
1140 | info->fix = sh_mobile_lcdc_fix; | ||
1141 | info->fix.line_length = lcd_cfg->xres * (cfg->bpp / 8); | ||
1142 | info->fix.smem_len = info->fix.line_length * | ||
1143 | var->yres_virtual; | ||
1144 | |||
1145 | buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, | 1247 | buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, |
1146 | &priv->ch[i].dma_handle, GFP_KERNEL); | 1248 | &ch->dma_handle, GFP_KERNEL); |
1147 | if (!buf) { | 1249 | if (!buf) { |
1148 | dev_err(&pdev->dev, "unable to allocate buffer\n"); | 1250 | dev_err(&pdev->dev, "unable to allocate buffer\n"); |
1149 | error = -ENOMEM; | 1251 | error = -ENOMEM; |
1150 | break; | 1252 | break; |
1151 | } | 1253 | } |
1152 | 1254 | ||
1153 | info->pseudo_palette = &priv->ch[i].pseudo_palette; | 1255 | info->pseudo_palette = &ch->pseudo_palette; |
1154 | info->flags = FBINFO_FLAG_DEFAULT; | 1256 | info->flags = FBINFO_FLAG_DEFAULT; |
1155 | 1257 | ||
1156 | error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); | 1258 | error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); |
1157 | if (error < 0) { | 1259 | if (error < 0) { |
1158 | dev_err(&pdev->dev, "unable to allocate cmap\n"); | 1260 | dev_err(&pdev->dev, "unable to allocate cmap\n"); |
1159 | dma_free_coherent(&pdev->dev, info->fix.smem_len, | 1261 | dma_free_coherent(&pdev->dev, info->fix.smem_len, |
1160 | buf, priv->ch[i].dma_handle); | 1262 | buf, ch->dma_handle); |
1161 | break; | 1263 | break; |
1162 | } | 1264 | } |
1163 | 1265 | ||
1164 | memset(buf, 0, info->fix.smem_len); | 1266 | info->fix.smem_start = ch->dma_handle; |
1165 | info->fix.smem_start = priv->ch[i].dma_handle; | 1267 | info->fix.line_length = var->xres * (cfg->bpp / 8); |
1166 | info->screen_base = buf; | 1268 | info->screen_base = buf; |
1167 | info->device = &pdev->dev; | 1269 | info->device = &pdev->dev; |
1168 | info->par = &priv->ch[i]; | 1270 | ch->display_var = *var; |
1169 | } | 1271 | } |
1170 | 1272 | ||
1171 | if (error) | 1273 | if (error) |
@@ -1179,6 +1281,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
1179 | 1281 | ||
1180 | for (i = 0; i < j; i++) { | 1282 | for (i = 0; i < j; i++) { |
1181 | struct sh_mobile_lcdc_chan *ch = priv->ch + i; | 1283 | struct sh_mobile_lcdc_chan *ch = priv->ch + i; |
1284 | const struct fb_videomode *mode = ch->cfg.lcd_cfg; | ||
1285 | |||
1286 | if (!mode) | ||
1287 | mode = &default_720p; | ||
1182 | 1288 | ||
1183 | info = ch->info; | 1289 | info = ch->info; |
1184 | 1290 | ||
@@ -1191,6 +1297,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
1191 | } | 1297 | } |
1192 | } | 1298 | } |
1193 | 1299 | ||
1300 | fb_videomode_to_modelist(mode, ch->cfg.num_cfg, &info->modelist); | ||
1194 | error = register_framebuffer(info); | 1301 | error = register_framebuffer(info); |
1195 | if (error < 0) | 1302 | if (error < 0) |
1196 | goto err1; | 1303 | goto err1; |
@@ -1200,8 +1307,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
1200 | pdev->name, | 1307 | pdev->name, |
1201 | (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? | 1308 | (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? |
1202 | "mainlcd" : "sublcd", | 1309 | "mainlcd" : "sublcd", |
1203 | (int) ch->cfg.lcd_cfg.xres, | 1310 | info->var.xres, info->var.yres, |
1204 | (int) ch->cfg.lcd_cfg.yres, | ||
1205 | ch->cfg.bpp); | 1311 | ch->cfg.bpp); |
1206 | 1312 | ||
1207 | /* deferred io mode: disable clock to save power */ | 1313 | /* deferred io mode: disable clock to save power */ |
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h new file mode 100644 index 000000000000..9ecee2fba1d7 --- /dev/null +++ b/drivers/video/sh_mobile_lcdcfb.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef SH_MOBILE_LCDCFB_H | ||
2 | #define SH_MOBILE_LCDCFB_H | ||
3 | |||
4 | #include <linux/completion.h> | ||
5 | #include <linux/fb.h> | ||
6 | #include <linux/mutex.h> | ||
7 | #include <linux/wait.h> | ||
8 | |||
9 | /* per-channel registers */ | ||
10 | enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, | ||
11 | LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, | ||
12 | LDHAJR, | ||
13 | NR_CH_REGS }; | ||
14 | |||
15 | #define PALETTE_NR 16 | ||
16 | |||
17 | struct sh_mobile_lcdc_priv; | ||
18 | struct fb_info; | ||
19 | |||
20 | struct sh_mobile_lcdc_chan { | ||
21 | struct sh_mobile_lcdc_priv *lcdc; | ||
22 | unsigned long *reg_offs; | ||
23 | unsigned long ldmt1r_value; | ||
24 | unsigned long enabled; /* ME and SE in LDCNT2R */ | ||
25 | struct sh_mobile_lcdc_chan_cfg cfg; | ||
26 | u32 pseudo_palette[PALETTE_NR]; | ||
27 | unsigned long saved_ch_regs[NR_CH_REGS]; | ||
28 | struct fb_info *info; | ||
29 | dma_addr_t dma_handle; | ||
30 | struct fb_deferred_io defio; | ||
31 | struct scatterlist *sglist; | ||
32 | unsigned long frame_end; | ||
33 | unsigned long pan_offset; | ||
34 | wait_queue_head_t frame_end_wait; | ||
35 | struct completion vsync_completion; | ||
36 | struct fb_var_screeninfo display_var; | ||
37 | int use_count; | ||
38 | struct mutex open_lock; /* protects the use counter */ | ||
39 | }; | ||
40 | |||
41 | #endif | ||