aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFlorian Tobias Schandinat <FlorianSchandinat@gmx.de>2012-03-13 19:17:43 -0400
committerFlorian Tobias Schandinat <FlorianSchandinat@gmx.de>2012-03-13 19:17:43 -0400
commitf9b4a5ce1a6b3154ff857e17d031473e0bac72d2 (patch)
tree247e94804b1abcfdfbd623b9b919ed9e2c76a994 /drivers
parentafb0499b0e43e2ee71a92bec9331635dc1173a07 (diff)
parentaf89956be14ae5bb304872756a47309edc2c94fb (diff)
Merge branch 'for-next' of git://linuxtv.org/pinchartl/fbdev into fbdev-next
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/Kconfig1
-rw-r--r--drivers/video/sh_mipi_dsi.c97
-rw-r--r--drivers/video/sh_mobile_hdmi.c297
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c1280
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h84
-rw-r--r--drivers/video/sh_mobile_meram.c690
6 files changed, 1297 insertions, 1152 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 8951cbd2d2fc..a43594243186 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2016,6 +2016,7 @@ config FB_SH_MOBILE_HDMI
2016config FB_SH_MOBILE_MERAM 2016config FB_SH_MOBILE_MERAM
2017 tristate "SuperH Mobile MERAM read ahead support for LCDC" 2017 tristate "SuperH Mobile MERAM read ahead support for LCDC"
2018 depends on FB_SH_MOBILE_LCDC 2018 depends on FB_SH_MOBILE_LCDC
2019 select GENERIC_ALLOCATOR
2019 default y 2020 default y
2020 ---help--- 2021 ---help---
2021 Enable MERAM support for the SH-Mobile LCD controller. 2022 Enable MERAM support for the SH-Mobile LCD controller.
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 05151b82f40f..42ad0f707e88 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -24,6 +24,8 @@
24#include <video/sh_mipi_dsi.h> 24#include <video/sh_mipi_dsi.h>
25#include <video/sh_mobile_lcdc.h> 25#include <video/sh_mobile_lcdc.h>
26 26
27#include "sh_mobile_lcdcfb.h"
28
27#define SYSCTRL 0x0000 29#define SYSCTRL 0x0000
28#define SYSCONF 0x0004 30#define SYSCONF 0x0004
29#define TIMSET 0x0008 31#define TIMSET 0x0008
@@ -50,16 +52,16 @@
50#define MAX_SH_MIPI_DSI 2 52#define MAX_SH_MIPI_DSI 2
51 53
52struct sh_mipi { 54struct sh_mipi {
55 struct sh_mobile_lcdc_entity entity;
56
53 void __iomem *base; 57 void __iomem *base;
54 void __iomem *linkbase; 58 void __iomem *linkbase;
55 struct clk *dsit_clk; 59 struct clk *dsit_clk;
56 struct platform_device *pdev; 60 struct platform_device *pdev;
57
58 void *next_board_data;
59 void (*next_display_on)(void *board_data, struct fb_info *info);
60 void (*next_display_off)(void *board_data);
61}; 61};
62 62
63#define to_sh_mipi(e) container_of(e, struct sh_mipi, entity)
64
63static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; 65static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI];
64 66
65/* Protect the above array */ 67/* Protect the above array */
@@ -120,7 +122,7 @@ static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable)
120 122
121static void sh_mipi_shutdown(struct platform_device *pdev) 123static void sh_mipi_shutdown(struct platform_device *pdev)
122{ 124{
123 struct sh_mipi *mipi = platform_get_drvdata(pdev); 125 struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev));
124 126
125 sh_mipi_dsi_enable(mipi, false); 127 sh_mipi_dsi_enable(mipi, false);
126} 128}
@@ -145,77 +147,77 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
145 pctype = 0; 147 pctype = 0;
146 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; 148 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
147 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; 149 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
148 linelength = ch->lcd_cfg[0].xres * 3; 150 linelength = ch->lcd_modes[0].xres * 3;
149 yuv = false; 151 yuv = false;
150 break; 152 break;
151 case MIPI_RGB565: 153 case MIPI_RGB565:
152 pctype = 1; 154 pctype = 1;
153 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; 155 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
154 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; 156 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
155 linelength = ch->lcd_cfg[0].xres * 2; 157 linelength = ch->lcd_modes[0].xres * 2;
156 yuv = false; 158 yuv = false;
157 break; 159 break;
158 case MIPI_RGB666_LP: 160 case MIPI_RGB666_LP:
159 pctype = 2; 161 pctype = 2;
160 datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; 162 datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
161 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; 163 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
162 linelength = ch->lcd_cfg[0].xres * 3; 164 linelength = ch->lcd_modes[0].xres * 3;
163 yuv = false; 165 yuv = false;
164 break; 166 break;
165 case MIPI_RGB666: 167 case MIPI_RGB666:
166 pctype = 3; 168 pctype = 3;
167 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; 169 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
168 pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; 170 pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
169 linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; 171 linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8;
170 yuv = false; 172 yuv = false;
171 break; 173 break;
172 case MIPI_BGR888: 174 case MIPI_BGR888:
173 pctype = 8; 175 pctype = 8;
174 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24; 176 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
175 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; 177 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
176 linelength = ch->lcd_cfg[0].xres * 3; 178 linelength = ch->lcd_modes[0].xres * 3;
177 yuv = false; 179 yuv = false;
178 break; 180 break;
179 case MIPI_BGR565: 181 case MIPI_BGR565:
180 pctype = 9; 182 pctype = 9;
181 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16; 183 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
182 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; 184 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
183 linelength = ch->lcd_cfg[0].xres * 2; 185 linelength = ch->lcd_modes[0].xres * 2;
184 yuv = false; 186 yuv = false;
185 break; 187 break;
186 case MIPI_BGR666_LP: 188 case MIPI_BGR666_LP:
187 pctype = 0xa; 189 pctype = 0xa;
188 datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18; 190 datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
189 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT; 191 pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
190 linelength = ch->lcd_cfg[0].xres * 3; 192 linelength = ch->lcd_modes[0].xres * 3;
191 yuv = false; 193 yuv = false;
192 break; 194 break;
193 case MIPI_BGR666: 195 case MIPI_BGR666:
194 pctype = 0xb; 196 pctype = 0xb;
195 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18; 197 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
196 pixfmt = MIPI_DCS_PIXEL_FMT_18BIT; 198 pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
197 linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8; 199 linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8;
198 yuv = false; 200 yuv = false;
199 break; 201 break;
200 case MIPI_YUYV: 202 case MIPI_YUYV:
201 pctype = 4; 203 pctype = 4;
202 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; 204 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
203 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; 205 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
204 linelength = ch->lcd_cfg[0].xres * 2; 206 linelength = ch->lcd_modes[0].xres * 2;
205 yuv = true; 207 yuv = true;
206 break; 208 break;
207 case MIPI_UYVY: 209 case MIPI_UYVY:
208 pctype = 5; 210 pctype = 5;
209 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16; 211 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
210 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT; 212 pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
211 linelength = ch->lcd_cfg[0].xres * 2; 213 linelength = ch->lcd_modes[0].xres * 2;
212 yuv = true; 214 yuv = true;
213 break; 215 break;
214 case MIPI_YUV420_L: 216 case MIPI_YUV420_L:
215 pctype = 6; 217 pctype = 6;
216 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; 218 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
217 pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; 219 pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
218 linelength = (ch->lcd_cfg[0].xres * 12 + 7) / 8; 220 linelength = (ch->lcd_modes[0].xres * 12 + 7) / 8;
219 yuv = true; 221 yuv = true;
220 break; 222 break;
221 case MIPI_YUV420: 223 case MIPI_YUV420:
@@ -223,7 +225,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
223 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12; 225 datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
224 pixfmt = MIPI_DCS_PIXEL_FMT_12BIT; 226 pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
225 /* Length of U/V line */ 227 /* Length of U/V line */
226 linelength = (ch->lcd_cfg[0].xres + 1) / 2; 228 linelength = (ch->lcd_modes[0].xres + 1) / 2;
227 yuv = true; 229 yuv = true;
228 break; 230 break;
229 default: 231 default:
@@ -292,7 +294,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
292 */ 294 */
293 iowrite32(0x00000006, mipi->linkbase + DTCTR); 295 iowrite32(0x00000006, mipi->linkbase + DTCTR);
294 /* VSYNC width = 2 (<< 17) */ 296 /* VSYNC width = 2 (<< 17) */
295 iowrite32((ch->lcd_cfg[0].vsync_len << pdata->vsynw_offset) | 297 iowrite32((ch->lcd_modes[0].vsync_len << pdata->vsynw_offset) |
296 (pdata->clksrc << 16) | (pctype << 12) | datatype, 298 (pdata->clksrc << 16) | (pctype << 12) | datatype,
297 mipi->linkbase + VMCTR1); 299 mipi->linkbase + VMCTR1);
298 300
@@ -326,7 +328,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
326 top = linelength << 16; /* RGBLEN */ 328 top = linelength << 16; /* RGBLEN */
327 bottom = 0x00000001; 329 bottom = 0x00000001;
328 if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */ 330 if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */
329 bottom = (pdata->lane * ch->lcd_cfg[0].hsync_len) - 10; 331 bottom = (pdata->lane * ch->lcd_modes[0].hsync_len) - 10;
330 iowrite32(top | bottom , mipi->linkbase + VMLEN1); 332 iowrite32(top | bottom , mipi->linkbase + VMLEN1);
331 333
332 /* 334 /*
@@ -346,18 +348,18 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
346 div = 2; 348 div = 2;
347 349
348 if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */ 350 if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */
349 top = ch->lcd_cfg[0].hsync_len + ch->lcd_cfg[0].left_margin; 351 top = ch->lcd_modes[0].hsync_len + ch->lcd_modes[0].left_margin;
350 top = ((pdata->lane * top / div) - 10) << 16; 352 top = ((pdata->lane * top / div) - 10) << 16;
351 } 353 }
352 if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */ 354 if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */
353 bottom = ch->lcd_cfg[0].right_margin; 355 bottom = ch->lcd_modes[0].right_margin;
354 bottom = (pdata->lane * bottom / div) - 12; 356 bottom = (pdata->lane * bottom / div) - 12;
355 } 357 }
356 358
357 bpp = linelength / ch->lcd_cfg[0].xres; /* byte / pixel */ 359 bpp = linelength / ch->lcd_modes[0].xres; /* byte / pixel */
358 if ((pdata->lane / div) > bpp) { 360 if ((pdata->lane / div) > bpp) {
359 tmp = ch->lcd_cfg[0].xres / bpp; /* output cycle */ 361 tmp = ch->lcd_modes[0].xres / bpp; /* output cycle */
360 tmp = ch->lcd_cfg[0].xres - tmp; /* (input - output) cycle */ 362 tmp = ch->lcd_modes[0].xres - tmp; /* (input - output) cycle */
361 delay = (pdata->lane * tmp); 363 delay = (pdata->lane * tmp);
362 } 364 }
363 365
@@ -392,9 +394,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
392 return 0; 394 return 0;
393} 395}
394 396
395static void mipi_display_on(void *arg, struct fb_info *info) 397static int mipi_display_on(struct sh_mobile_lcdc_entity *entity)
396{ 398{
397 struct sh_mipi *mipi = arg; 399 struct sh_mipi *mipi = to_sh_mipi(entity);
398 struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; 400 struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data;
399 int ret; 401 int ret;
400 402
@@ -410,25 +412,21 @@ static void mipi_display_on(void *arg, struct fb_info *info)
410 412
411 sh_mipi_dsi_enable(mipi, true); 413 sh_mipi_dsi_enable(mipi, true);
412 414
413 if (mipi->next_display_on) 415 return SH_MOBILE_LCDC_DISPLAY_CONNECTED;
414 mipi->next_display_on(mipi->next_board_data, info);
415
416 return;
417 416
418mipi_display_on_fail1: 417mipi_display_on_fail1:
419 pm_runtime_put_sync(&mipi->pdev->dev); 418 pm_runtime_put_sync(&mipi->pdev->dev);
420mipi_display_on_fail2: 419mipi_display_on_fail2:
421 pdata->set_dot_clock(mipi->pdev, mipi->base, 0); 420 pdata->set_dot_clock(mipi->pdev, mipi->base, 0);
421
422 return ret;
422} 423}
423 424
424static void mipi_display_off(void *arg) 425static void mipi_display_off(struct sh_mobile_lcdc_entity *entity)
425{ 426{
426 struct sh_mipi *mipi = arg; 427 struct sh_mipi *mipi = to_sh_mipi(entity);
427 struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data; 428 struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data;
428 429
429 if (mipi->next_display_off)
430 mipi->next_display_off(mipi->next_board_data);
431
432 sh_mipi_dsi_enable(mipi, false); 430 sh_mipi_dsi_enable(mipi, false);
433 431
434 pdata->set_dot_clock(mipi->pdev, mipi->base, 0); 432 pdata->set_dot_clock(mipi->pdev, mipi->base, 0);
@@ -436,6 +434,11 @@ static void mipi_display_off(void *arg)
436 pm_runtime_put_sync(&mipi->pdev->dev); 434 pm_runtime_put_sync(&mipi->pdev->dev);
437} 435}
438 436
437static const struct sh_mobile_lcdc_entity_ops mipi_ops = {
438 .display_on = mipi_display_on,
439 .display_off = mipi_display_off,
440};
441
439static int __init sh_mipi_probe(struct platform_device *pdev) 442static int __init sh_mipi_probe(struct platform_device *pdev)
440{ 443{
441 struct sh_mipi *mipi; 444 struct sh_mipi *mipi;
@@ -467,6 +470,9 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
467 goto ealloc; 470 goto ealloc;
468 } 471 }
469 472
473 mipi->entity.owner = THIS_MODULE;
474 mipi->entity.ops = &mipi_ops;
475
470 if (!request_mem_region(res->start, resource_size(res), pdev->name)) { 476 if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
471 dev_err(&pdev->dev, "MIPI register region already claimed\n"); 477 dev_err(&pdev->dev, "MIPI register region already claimed\n");
472 ret = -EBUSY; 478 ret = -EBUSY;
@@ -521,18 +527,7 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
521 pm_runtime_resume(&pdev->dev); 527 pm_runtime_resume(&pdev->dev);
522 528
523 mutex_unlock(&array_lock); 529 mutex_unlock(&array_lock);
524 platform_set_drvdata(pdev, mipi); 530 platform_set_drvdata(pdev, &mipi->entity);
525
526 /* Save original LCDC callbacks */
527 mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data;
528 mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on;
529 mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off;
530
531 /* Set up LCDC callbacks */
532 pdata->lcd_chan->board_cfg.board_data = mipi;
533 pdata->lcd_chan->board_cfg.display_on = mipi_display_on;
534 pdata->lcd_chan->board_cfg.display_off = mipi_display_off;
535 pdata->lcd_chan->board_cfg.owner = THIS_MODULE;
536 531
537 return 0; 532 return 0;
538 533
@@ -558,10 +553,9 @@ efindslot:
558 553
559static int __exit sh_mipi_remove(struct platform_device *pdev) 554static int __exit sh_mipi_remove(struct platform_device *pdev)
560{ 555{
561 struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
562 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 556 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
563 struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); 557 struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
564 struct sh_mipi *mipi = platform_get_drvdata(pdev); 558 struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev));
565 int i, ret; 559 int i, ret;
566 560
567 mutex_lock(&array_lock); 561 mutex_lock(&array_lock);
@@ -581,11 +575,6 @@ static int __exit sh_mipi_remove(struct platform_device *pdev)
581 if (ret < 0) 575 if (ret < 0)
582 return ret; 576 return ret;
583 577
584 pdata->lcd_chan->board_cfg.owner = NULL;
585 pdata->lcd_chan->board_cfg.display_on = NULL;
586 pdata->lcd_chan->board_cfg.display_off = NULL;
587 pdata->lcd_chan->board_cfg.board_data = NULL;
588
589 pm_runtime_disable(&pdev->dev); 578 pm_runtime_disable(&pdev->dev);
590 clk_disable(mipi->dsit_clk); 579 clk_disable(mipi->dsit_clk);
591 clk_put(mipi->dsit_clk); 580 clk_put(mipi->dsit_clk);
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c
index 647ba984f00f..eafb19da2c07 100644
--- a/drivers/video/sh_mobile_hdmi.c
+++ b/drivers/video/sh_mobile_hdmi.c
@@ -208,6 +208,8 @@ enum hotplug_state {
208}; 208};
209 209
210struct sh_hdmi { 210struct sh_hdmi {
211 struct sh_mobile_lcdc_entity entity;
212
211 void __iomem *base; 213 void __iomem *base;
212 enum hotplug_state hp_state; /* hot-plug status */ 214 enum hotplug_state hp_state; /* hot-plug status */
213 u8 preprogrammed_vic; /* use a pre-programmed VIC or 215 u8 preprogrammed_vic; /* use a pre-programmed VIC or
@@ -217,14 +219,13 @@ struct sh_hdmi {
217 u8 edid_blocks; 219 u8 edid_blocks;
218 struct clk *hdmi_clk; 220 struct clk *hdmi_clk;
219 struct device *dev; 221 struct device *dev;
220 struct fb_info *info;
221 struct mutex mutex; /* Protect the info pointer */
222 struct delayed_work edid_work; 222 struct delayed_work edid_work;
223 struct fb_var_screeninfo var; 223 struct fb_videomode mode;
224 struct fb_monspecs monspec; 224 struct fb_monspecs monspec;
225 struct notifier_block notifier;
226}; 225};
227 226
227#define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity)
228
228static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg) 229static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg)
229{ 230{
230 iowrite8(data, hdmi->base + reg); 231 iowrite8(data, hdmi->base + reg);
@@ -290,24 +291,24 @@ static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = {
290/* External video parameter settings */ 291/* External video parameter settings */
291static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) 292static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
292{ 293{
293 struct fb_var_screeninfo *var = &hdmi->var; 294 struct fb_videomode *mode = &hdmi->mode;
294 u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset; 295 u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset;
295 u8 sync = 0; 296 u8 sync = 0;
296 297
297 htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len; 298 htotal = mode->xres + mode->right_margin + mode->left_margin
298 299 + mode->hsync_len;
299 hdelay = var->hsync_len + var->left_margin; 300 hdelay = mode->hsync_len + mode->left_margin;
300 hblank = var->right_margin + hdelay; 301 hblank = mode->right_margin + hdelay;
301 302
302 /* 303 /*
303 * Vertical timing looks a bit different in Figure 18, 304 * Vertical timing looks a bit different in Figure 18,
304 * but let's try the same first by setting offset = 0 305 * but let's try the same first by setting offset = 0
305 */ 306 */
306 vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; 307 vtotal = mode->yres + mode->upper_margin + mode->lower_margin
307 308 + mode->vsync_len;
308 vdelay = var->vsync_len + var->upper_margin; 309 vdelay = mode->vsync_len + mode->upper_margin;
309 vblank = var->lower_margin + vdelay; 310 vblank = mode->lower_margin + vdelay;
310 voffset = min(var->upper_margin / 2, 6U); 311 voffset = min(mode->upper_margin / 2, 6U);
311 312
312 /* 313 /*
313 * [3]: VSYNC polarity: Positive 314 * [3]: VSYNC polarity: Positive
@@ -315,14 +316,14 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
315 * [1]: Interlace/Progressive: Progressive 316 * [1]: Interlace/Progressive: Progressive
316 * [0]: External video settings enable: used. 317 * [0]: External video settings enable: used.
317 */ 318 */
318 if (var->sync & FB_SYNC_HOR_HIGH_ACT) 319 if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
319 sync |= 4; 320 sync |= 4;
320 if (var->sync & FB_SYNC_VERT_HIGH_ACT) 321 if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
321 sync |= 8; 322 sync |= 8;
322 323
323 dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n", 324 dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n",
324 htotal, hblank, hdelay, var->hsync_len, 325 htotal, hblank, hdelay, mode->hsync_len,
325 vtotal, vblank, vdelay, var->vsync_len, sync); 326 vtotal, vblank, vdelay, mode->vsync_len, sync);
326 327
327 hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); 328 hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS);
328 329
@@ -335,8 +336,8 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
335 hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0); 336 hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0);
336 hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8); 337 hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8);
337 338
338 hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0); 339 hdmi_write(hdmi, mode->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0);
339 hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8); 340 hdmi_write(hdmi, mode->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8);
340 341
341 hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0); 342 hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0);
342 hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8); 343 hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8);
@@ -345,7 +346,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
345 346
346 hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY); 347 hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY);
347 348
348 hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); 349 hdmi_write(hdmi, mode->vsync_len, HDMI_EXTERNAL_V_DURATION);
349 350
350 /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ 351 /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */
351 if (!hdmi->preprogrammed_vic) 352 if (!hdmi->preprogrammed_vic)
@@ -472,7 +473,7 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
472 */ 473 */
473static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) 474static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
474{ 475{
475 if (hdmi->var.pixclock < 10000) { 476 if (hdmi->mode.pixclock < 10000) {
476 /* for 1080p8bit 148MHz */ 477 /* for 1080p8bit 148MHz */
477 hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); 478 hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1);
478 hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); 479 hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2);
@@ -483,7 +484,7 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
483 hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); 484 hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8);
484 hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); 485 hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9);
485 hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); 486 hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10);
486 } else if (hdmi->var.pixclock < 30000) { 487 } else if (hdmi->mode.pixclock < 30000) {
487 /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ 488 /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */
488 /* 489 /*
489 * [1:0] Speed_A 490 * [1:0] Speed_A
@@ -732,14 +733,12 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
732static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, 733static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
733 unsigned long *parent_rate) 734 unsigned long *parent_rate)
734{ 735{
735 struct fb_var_screeninfo tmpvar; 736 struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc;
736 struct fb_var_screeninfo *var = &tmpvar;
737 const struct fb_videomode *mode, *found = NULL; 737 const struct fb_videomode *mode, *found = NULL;
738 struct fb_info *info = hdmi->info;
739 struct fb_modelist *modelist = NULL;
740 unsigned int f_width = 0, f_height = 0, f_refresh = 0; 738 unsigned int f_width = 0, f_height = 0, f_refresh = 0;
741 unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */ 739 unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */
742 bool scanning = false, preferred_bad = false; 740 bool scanning = false, preferred_bad = false;
741 bool use_edid_mode = false;
743 u8 edid[128]; 742 u8 edid[128];
744 char *forced; 743 char *forced;
745 int i; 744 int i;
@@ -854,12 +853,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
854 } 853 }
855 854
856 /* Check if supported: sufficient fb memory, supported clock-rate */ 855 /* Check if supported: sufficient fb memory, supported clock-rate */
857 fb_videomode_to_var(var, mode); 856 if (ch && ch->notify &&
858 857 ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_MODE, mode,
859 var->bits_per_pixel = info->var.bits_per_pixel; 858 NULL)) {
860
861 if (info && info->fbops->fb_check_var &&
862 info->fbops->fb_check_var(var, info)) {
863 scanning = true; 859 scanning = true;
864 preferred_bad = true; 860 preferred_bad = true;
865 continue; 861 continue;
@@ -867,28 +863,19 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
867 863
868 found = mode; 864 found = mode;
869 found_rate_error = rate_error; 865 found_rate_error = rate_error;
866 use_edid_mode = true;
870 } 867 }
871 868
872 hdmi->var.width = hdmi->monspec.max_x * 10;
873 hdmi->var.height = hdmi->monspec.max_y * 10;
874
875 /* 869 /*
876 * TODO 1: if no ->info is present, postpone running the config until 870 * TODO 1: if no default mode is present, postpone running the config
877 * after ->info first gets registered. 871 * until after the LCDC channel is initialized.
878 * TODO 2: consider registering the HDMI platform device from the LCDC 872 * TODO 2: consider registering the HDMI platform device from the LCDC
879 * driver, and passing ->info with HDMI platform data. 873 * driver.
880 */ 874 */
881 if (info && !found) { 875 if (!found && hdmi->entity.def_mode.xres != 0) {
882 modelist = info->modelist.next && 876 found = &hdmi->entity.def_mode;
883 !list_empty(&info->modelist) ? 877 found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate,
884 list_entry(info->modelist.next, 878 parent_rate);
885 struct fb_modelist, list) :
886 NULL;
887
888 if (modelist) {
889 found = &modelist->mode;
890 found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate);
891 }
892 } 879 }
893 880
894 /* No cookie today */ 881 /* No cookie today */
@@ -912,12 +899,13 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
912 else 899 else
913 hdmi->preprogrammed_vic = 0; 900 hdmi->preprogrammed_vic = 0;
914 901
915 dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", 902 dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), "
916 modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", 903 "clock error %luHz\n", use_edid_mode ? "EDID" : "default",
917 found->xres, found->yres, found->refresh, 904 hdmi->preprogrammed_vic ? "VIC" : "external", found->xres,
918 PICOS2KHZ(found->pixclock) * 1000, found_rate_error); 905 found->yres, found->refresh, PICOS2KHZ(found->pixclock) * 1000,
906 found_rate_error);
919 907
920 fb_videomode_to_var(&hdmi->var, found); 908 hdmi->mode = *found;
921 sh_hdmi_external_video_param(hdmi); 909 sh_hdmi_external_video_param(hdmi);
922 910
923 return 0; 911 return 0;
@@ -998,22 +986,12 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id)
998 return IRQ_HANDLED; 986 return IRQ_HANDLED;
999} 987}
1000 988
1001/* locking: called with info->lock held, or before register_framebuffer() */ 989static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity)
1002static void sh_hdmi_display_on(void *arg, struct fb_info *info)
1003{ 990{
1004 /* 991 struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity);
1005 * info is guaranteed to be valid, when we are called, because our
1006 * FB_EVENT_FB_UNBIND notify is also called with info->lock held
1007 */
1008 struct sh_hdmi *hdmi = arg;
1009 struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
1010 struct sh_mobile_lcdc_chan *ch = info->par;
1011 992
1012 dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, 993 dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, hdmi,
1013 pdata->lcd_dev, info->state); 994 hdmi->hp_state);
1014
1015 /* No need to lock */
1016 hdmi->info = info;
1017 995
1018 /* 996 /*
1019 * hp_state can be set to 997 * hp_state can be set to
@@ -1021,56 +999,30 @@ static void sh_hdmi_display_on(void *arg, struct fb_info *info)
1021 * HDMI_HOTPLUG_CONNECTED: on monitor plug-in 999 * HDMI_HOTPLUG_CONNECTED: on monitor plug-in
1022 * HDMI_HOTPLUG_EDID_DONE: on EDID read completion 1000 * HDMI_HOTPLUG_EDID_DONE: on EDID read completion
1023 */ 1001 */
1024 switch (hdmi->hp_state) { 1002 if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) {
1025 case HDMI_HOTPLUG_EDID_DONE:
1026 /* PS mode d->e. All functions are active */ 1003 /* PS mode d->e. All functions are active */
1027 hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL); 1004 hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL);
1028 dev_dbg(hdmi->dev, "HDMI running\n"); 1005 dev_dbg(hdmi->dev, "HDMI running\n");
1029 break;
1030 case HDMI_HOTPLUG_DISCONNECTED:
1031 info->state = FBINFO_STATE_SUSPENDED;
1032 default:
1033 hdmi->var = ch->display_var;
1034 } 1006 }
1007
1008 return hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED
1009 ? SH_MOBILE_LCDC_DISPLAY_DISCONNECTED
1010 : SH_MOBILE_LCDC_DISPLAY_CONNECTED;
1035} 1011}
1036 1012
1037/* locking: called with info->lock held */ 1013static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity)
1038static void sh_hdmi_display_off(void *arg)
1039{ 1014{
1040 struct sh_hdmi *hdmi = arg; 1015 struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity);
1041 struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
1042 1016
1043 dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev); 1017 dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi);
1044 /* PS mode e->a */ 1018 /* PS mode e->a */
1045 hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL); 1019 hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL);
1046} 1020}
1047 1021
1048static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) 1022static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = {
1049{ 1023 .display_on = sh_hdmi_display_on,
1050 struct fb_info *info = hdmi->info; 1024 .display_off = sh_hdmi_display_off,
1051 struct sh_mobile_lcdc_chan *ch = info->par; 1025};
1052 struct fb_var_screeninfo *new_var = &hdmi->var, *old_var = &ch->display_var;
1053 struct fb_videomode mode1, mode2;
1054
1055 fb_var_to_videomode(&mode1, old_var);
1056 fb_var_to_videomode(&mode2, new_var);
1057
1058 dev_dbg(info->dev, "Old %ux%u, new %ux%u\n",
1059 mode1.xres, mode1.yres, mode2.xres, mode2.yres);
1060
1061 if (fb_mode_is_equal(&mode1, &mode2)) {
1062 /* It can be a different monitor with an equal video-mode */
1063 old_var->width = new_var->width;
1064 old_var->height = new_var->height;
1065 return false;
1066 }
1067
1068 dev_dbg(info->dev, "Switching %u -> %u lines\n",
1069 mode1.yres, mode2.yres);
1070 *old_var = *new_var;
1071
1072 return true;
1073}
1074 1026
1075/** 1027/**
1076 * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock 1028 * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock
@@ -1111,20 +1063,11 @@ static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate,
1111static void sh_hdmi_edid_work_fn(struct work_struct *work) 1063static void sh_hdmi_edid_work_fn(struct work_struct *work)
1112{ 1064{
1113 struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work); 1065 struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work);
1114 struct fb_info *info; 1066 struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc;
1115 struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
1116 struct sh_mobile_lcdc_chan *ch;
1117 int ret; 1067 int ret;
1118 1068
1119 dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, 1069 dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, hdmi,
1120 pdata->lcd_dev, hdmi->hp_state); 1070 hdmi->hp_state);
1121
1122 if (!pdata->lcd_dev)
1123 return;
1124
1125 mutex_lock(&hdmi->mutex);
1126
1127 info = hdmi->info;
1128 1071
1129 if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { 1072 if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) {
1130 unsigned long parent_rate = 0, hdmi_rate; 1073 unsigned long parent_rate = 0, hdmi_rate;
@@ -1145,103 +1088,32 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
1145 /* Switched to another (d) power-save mode */ 1088 /* Switched to another (d) power-save mode */
1146 msleep(10); 1089 msleep(10);
1147 1090
1148 if (!info) 1091 if (ch && ch->notify)
1149 goto out; 1092 ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT,
1150 1093 &hdmi->mode, &hdmi->monspec);
1151 ch = info->par;
1152
1153 if (lock_fb_info(info)) {
1154 console_lock();
1155
1156 /* HDMI plug in */
1157 if (!sh_hdmi_must_reconfigure(hdmi) &&
1158 info->state == FBINFO_STATE_RUNNING) {
1159 /*
1160 * First activation with the default monitor - just turn
1161 * on, if we run a resume here, the logo disappears
1162 */
1163 info->var.width = hdmi->var.width;
1164 info->var.height = hdmi->var.height;
1165 sh_hdmi_display_on(hdmi, info);
1166 } else {
1167 /* New monitor or have to wake up */
1168 fb_set_suspend(info, 0);
1169 }
1170
1171 console_unlock();
1172 unlock_fb_info(info);
1173 }
1174 } else { 1094 } else {
1175 ret = 0;
1176 if (!info)
1177 goto out;
1178
1179 hdmi->monspec.modedb_len = 0; 1095 hdmi->monspec.modedb_len = 0;
1180 fb_destroy_modedb(hdmi->monspec.modedb); 1096 fb_destroy_modedb(hdmi->monspec.modedb);
1181 hdmi->monspec.modedb = NULL; 1097 hdmi->monspec.modedb = NULL;
1182 1098
1183 if (lock_fb_info(info)) { 1099 if (ch && ch->notify)
1184 console_lock(); 1100 ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT,
1101 NULL, NULL);
1185 1102
1186 /* HDMI disconnect */ 1103 ret = 0;
1187 fb_set_suspend(info, 1);
1188
1189 console_unlock();
1190 unlock_fb_info(info);
1191 }
1192 } 1104 }
1193 1105
1194out: 1106out:
1195 if (ret < 0 && ret != -EAGAIN) 1107 if (ret < 0 && ret != -EAGAIN)
1196 hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; 1108 hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED;
1197 mutex_unlock(&hdmi->mutex);
1198 1109
1199 dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev); 1110 dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi);
1200}
1201
1202static int sh_hdmi_notify(struct notifier_block *nb,
1203 unsigned long action, void *data)
1204{
1205 struct fb_event *event = data;
1206 struct fb_info *info = event->info;
1207 struct sh_mobile_lcdc_chan *ch = info->par;
1208 struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
1209 struct sh_hdmi *hdmi = board_cfg->board_data;
1210
1211 if (!hdmi || nb != &hdmi->notifier || hdmi->info != info)
1212 return NOTIFY_DONE;
1213
1214 switch(action) {
1215 case FB_EVENT_FB_REGISTERED:
1216 /* Unneeded, activation taken care by sh_hdmi_display_on() */
1217 break;
1218 case FB_EVENT_FB_UNREGISTERED:
1219 /*
1220 * We are called from unregister_framebuffer() with the
1221 * info->lock held. This is bad for us, because we can race with
1222 * the scheduled work, which has to call fb_set_suspend(), which
1223 * takes info->lock internally, so, sh_hdmi_edid_work_fn()
1224 * cannot take and hold info->lock for the whole function
1225 * duration. Using an additional lock creates a classical AB-BA
1226 * lock up. Therefore, we have to release the info->lock
1227 * temporarily, synchronise with the work queue and re-acquire
1228 * the info->lock.
1229 */
1230 unlock_fb_info(info);
1231 mutex_lock(&hdmi->mutex);
1232 hdmi->info = NULL;
1233 mutex_unlock(&hdmi->mutex);
1234 lock_fb_info(info);
1235 return NOTIFY_OK;
1236 }
1237 return NOTIFY_DONE;
1238} 1111}
1239 1112
1240static int __init sh_hdmi_probe(struct platform_device *pdev) 1113static int __init sh_hdmi_probe(struct platform_device *pdev)
1241{ 1114{
1242 struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; 1115 struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
1243 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1116 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1244 struct sh_mobile_lcdc_board_cfg *board_cfg;
1245 int irq = platform_get_irq(pdev, 0), ret; 1117 int irq = platform_get_irq(pdev, 0), ret;
1246 struct sh_hdmi *hdmi; 1118 struct sh_hdmi *hdmi;
1247 long rate; 1119 long rate;
@@ -1255,9 +1127,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
1255 return -ENOMEM; 1127 return -ENOMEM;
1256 } 1128 }
1257 1129
1258 mutex_init(&hdmi->mutex);
1259
1260 hdmi->dev = &pdev->dev; 1130 hdmi->dev = &pdev->dev;
1131 hdmi->entity.owner = THIS_MODULE;
1132 hdmi->entity.ops = &sh_hdmi_ops;
1261 1133
1262 hdmi->hdmi_clk = clk_get(&pdev->dev, "ick"); 1134 hdmi->hdmi_clk = clk_get(&pdev->dev, "ick");
1263 if (IS_ERR(hdmi->hdmi_clk)) { 1135 if (IS_ERR(hdmi->hdmi_clk)) {
@@ -1297,14 +1169,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
1297 goto emap; 1169 goto emap;
1298 } 1170 }
1299 1171
1300 platform_set_drvdata(pdev, hdmi); 1172 platform_set_drvdata(pdev, &hdmi->entity);
1301
1302 /* Set up LCDC callbacks */
1303 board_cfg = &pdata->lcd_chan->board_cfg;
1304 board_cfg->owner = THIS_MODULE;
1305 board_cfg->board_data = hdmi;
1306 board_cfg->display_on = sh_hdmi_display_on;
1307 board_cfg->display_off = sh_hdmi_display_off;
1308 1173
1309 INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn); 1174 INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn);
1310 1175
@@ -1329,9 +1194,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
1329 goto ecodec; 1194 goto ecodec;
1330 } 1195 }
1331 1196
1332 hdmi->notifier.notifier_call = sh_hdmi_notify;
1333 fb_register_client(&hdmi->notifier);
1334
1335 return 0; 1197 return 0;
1336 1198
1337ecodec: 1199ecodec:
@@ -1347,7 +1209,6 @@ ereqreg:
1347erate: 1209erate:
1348 clk_put(hdmi->hdmi_clk); 1210 clk_put(hdmi->hdmi_clk);
1349egetclk: 1211egetclk:
1350 mutex_destroy(&hdmi->mutex);
1351 kfree(hdmi); 1212 kfree(hdmi);
1352 1213
1353 return ret; 1214 return ret;
@@ -1355,21 +1216,12 @@ egetclk:
1355 1216
1356static int __exit sh_hdmi_remove(struct platform_device *pdev) 1217static int __exit sh_hdmi_remove(struct platform_device *pdev)
1357{ 1218{
1358 struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data; 1219 struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev));
1359 struct sh_hdmi *hdmi = platform_get_drvdata(pdev);
1360 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1220 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1361 struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg;
1362 int irq = platform_get_irq(pdev, 0); 1221 int irq = platform_get_irq(pdev, 0);
1363 1222
1364 snd_soc_unregister_codec(&pdev->dev); 1223 snd_soc_unregister_codec(&pdev->dev);
1365 1224
1366 fb_unregister_client(&hdmi->notifier);
1367
1368 board_cfg->display_on = NULL;
1369 board_cfg->display_off = NULL;
1370 board_cfg->board_data = NULL;
1371 board_cfg->owner = NULL;
1372
1373 /* No new work will be scheduled, wait for running ISR */ 1225 /* No new work will be scheduled, wait for running ISR */
1374 free_irq(irq, hdmi); 1226 free_irq(irq, hdmi);
1375 /* Wait for already scheduled work */ 1227 /* Wait for already scheduled work */
@@ -1380,7 +1232,6 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev)
1380 clk_put(hdmi->hdmi_clk); 1232 clk_put(hdmi->hdmi_clk);
1381 iounmap(hdmi->base); 1233 iounmap(hdmi->base);
1382 release_mem_region(res->start, resource_size(res)); 1234 release_mem_region(res->start, resource_size(res));
1383 mutex_destroy(&hdmi->mutex);
1384 kfree(hdmi); 1235 kfree(hdmi);
1385 1236
1386 return 0; 1237 return 0;
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index aac5b369d73c..7a0b301587f6 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -8,26 +8,27 @@
8 * for more details. 8 * for more details.
9 */ 9 */
10 10
11#include <linux/kernel.h> 11#include <linux/atomic.h>
12#include <linux/init.h> 12#include <linux/backlight.h>
13#include <linux/delay.h>
14#include <linux/mm.h>
15#include <linux/clk.h> 13#include <linux/clk.h>
16#include <linux/pm_runtime.h> 14#include <linux/console.h>
17#include <linux/platform_device.h>
18#include <linux/dma-mapping.h> 15#include <linux/dma-mapping.h>
16#include <linux/delay.h>
17#include <linux/gpio.h>
18#include <linux/init.h>
19#include <linux/interrupt.h> 19#include <linux/interrupt.h>
20#include <linux/videodev2.h>
21#include <linux/vmalloc.h>
22#include <linux/ioctl.h> 20#include <linux/ioctl.h>
23#include <linux/slab.h> 21#include <linux/kernel.h>
24#include <linux/console.h> 22#include <linux/mm.h>
25#include <linux/backlight.h>
26#include <linux/gpio.h>
27#include <linux/module.h> 23#include <linux/module.h>
24#include <linux/platform_device.h>
25#include <linux/pm_runtime.h>
26#include <linux/slab.h>
27#include <linux/videodev2.h>
28#include <linux/vmalloc.h>
29
28#include <video/sh_mobile_lcdc.h> 30#include <video/sh_mobile_lcdc.h>
29#include <video/sh_mobile_meram.h> 31#include <video/sh_mobile_meram.h>
30#include <linux/atomic.h>
31 32
32#include "sh_mobile_lcdcfb.h" 33#include "sh_mobile_lcdcfb.h"
33 34
@@ -37,6 +38,24 @@
37#define MAX_XRES 1920 38#define MAX_XRES 1920
38#define MAX_YRES 1080 39#define MAX_YRES 1080
39 40
41struct sh_mobile_lcdc_priv {
42 void __iomem *base;
43 int irq;
44 atomic_t hw_usecnt;
45 struct device *dev;
46 struct clk *dot_clk;
47 unsigned long lddckr;
48 struct sh_mobile_lcdc_chan ch[2];
49 struct notifier_block notifier;
50 int started;
51 int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
52 struct sh_mobile_meram_info *meram_dev;
53};
54
55/* -----------------------------------------------------------------------------
56 * Registers access
57 */
58
40static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { 59static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
41 [LDDCKPAT1R] = 0x400, 60 [LDDCKPAT1R] = 0x400,
42 [LDDCKPAT2R] = 0x404, 61 [LDDCKPAT2R] = 0x404,
@@ -75,38 +94,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
75 [LDPMR] = 0x63c, 94 [LDPMR] = 0x63c,
76}; 95};
77 96
78static const struct fb_videomode default_720p = {
79 .name = "HDMI 720p",
80 .xres = 1280,
81 .yres = 720,
82
83 .left_margin = 220,
84 .right_margin = 110,
85 .hsync_len = 40,
86
87 .upper_margin = 20,
88 .lower_margin = 5,
89 .vsync_len = 5,
90
91 .pixclock = 13468,
92 .refresh = 60,
93 .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
94};
95
96struct sh_mobile_lcdc_priv {
97 void __iomem *base;
98 int irq;
99 atomic_t hw_usecnt;
100 struct device *dev;
101 struct clk *dot_clk;
102 unsigned long lddckr;
103 struct sh_mobile_lcdc_chan ch[2];
104 struct notifier_block notifier;
105 int started;
106 int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
107 struct sh_mobile_meram_info *meram_dev;
108};
109
110static bool banked(int reg_nr) 97static bool banked(int reg_nr)
111{ 98{
112 switch (reg_nr) { 99 switch (reg_nr) {
@@ -127,6 +114,11 @@ static bool banked(int reg_nr)
127 return false; 114 return false;
128} 115}
129 116
117static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
118{
119 return chan->cfg->chan == LCDC_CHAN_SUBLCD;
120}
121
130static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, 122static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
131 int reg_nr, unsigned long data) 123 int reg_nr, unsigned long data)
132{ 124{
@@ -169,11 +161,72 @@ static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv,
169 cpu_relax(); 161 cpu_relax();
170} 162}
171 163
172static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) 164/* -----------------------------------------------------------------------------
165 * Clock management
166 */
167
168static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
169{
170 if (atomic_inc_and_test(&priv->hw_usecnt)) {
171 if (priv->dot_clk)
172 clk_enable(priv->dot_clk);
173 pm_runtime_get_sync(priv->dev);
174 if (priv->meram_dev && priv->meram_dev->pdev)
175 pm_runtime_get_sync(&priv->meram_dev->pdev->dev);
176 }
177}
178
179static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
173{ 180{
174 return chan->cfg.chan == LCDC_CHAN_SUBLCD; 181 if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
182 if (priv->meram_dev && priv->meram_dev->pdev)
183 pm_runtime_put_sync(&priv->meram_dev->pdev->dev);
184 pm_runtime_put(priv->dev);
185 if (priv->dot_clk)
186 clk_disable(priv->dot_clk);
187 }
175} 188}
176 189
190static int sh_mobile_lcdc_setup_clocks(struct sh_mobile_lcdc_priv *priv,
191 int clock_source)
192{
193 struct clk *clk;
194 char *str;
195
196 switch (clock_source) {
197 case LCDC_CLK_BUS:
198 str = "bus_clk";
199 priv->lddckr = LDDCKR_ICKSEL_BUS;
200 break;
201 case LCDC_CLK_PERIPHERAL:
202 str = "peripheral_clk";
203 priv->lddckr = LDDCKR_ICKSEL_MIPI;
204 break;
205 case LCDC_CLK_EXTERNAL:
206 str = NULL;
207 priv->lddckr = LDDCKR_ICKSEL_HDMI;
208 break;
209 default:
210 return -EINVAL;
211 }
212
213 if (str == NULL)
214 return 0;
215
216 clk = clk_get(priv->dev, str);
217 if (IS_ERR(clk)) {
218 dev_err(priv->dev, "cannot get dot clock %s\n", str);
219 return PTR_ERR(clk);
220 }
221
222 priv->dot_clk = clk;
223 return 0;
224}
225
226/* -----------------------------------------------------------------------------
227 * Display, panel and deferred I/O
228 */
229
177static void lcdc_sys_write_index(void *handle, unsigned long data) 230static void lcdc_sys_write_index(void *handle, unsigned long data)
178{ 231{
179 struct sh_mobile_lcdc_chan *ch = handle; 232 struct sh_mobile_lcdc_chan *ch = handle;
@@ -216,74 +269,11 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
216 lcdc_sys_read_data, 269 lcdc_sys_read_data,
217}; 270};
218 271
219static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var)
220{
221 if (var->grayscale > 1)
222 return var->grayscale;
223
224 switch (var->bits_per_pixel) {
225 case 16:
226 return V4L2_PIX_FMT_RGB565;
227 case 24:
228 return V4L2_PIX_FMT_BGR24;
229 case 32:
230 return V4L2_PIX_FMT_BGR32;
231 default:
232 return 0;
233 }
234}
235
236static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var)
237{
238 return var->grayscale > 1;
239}
240
241static bool sh_mobile_format_is_yuv(const struct fb_var_screeninfo *var)
242{
243 if (var->grayscale <= 1)
244 return false;
245
246 switch (var->grayscale) {
247 case V4L2_PIX_FMT_NV12:
248 case V4L2_PIX_FMT_NV21:
249 case V4L2_PIX_FMT_NV16:
250 case V4L2_PIX_FMT_NV61:
251 case V4L2_PIX_FMT_NV24:
252 case V4L2_PIX_FMT_NV42:
253 return true;
254
255 default:
256 return false;
257 }
258}
259
260static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
261{
262 if (atomic_inc_and_test(&priv->hw_usecnt)) {
263 if (priv->dot_clk)
264 clk_enable(priv->dot_clk);
265 pm_runtime_get_sync(priv->dev);
266 if (priv->meram_dev && priv->meram_dev->pdev)
267 pm_runtime_get_sync(&priv->meram_dev->pdev->dev);
268 }
269}
270
271static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
272{
273 if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
274 if (priv->meram_dev && priv->meram_dev->pdev)
275 pm_runtime_put_sync(&priv->meram_dev->pdev->dev);
276 pm_runtime_put(priv->dev);
277 if (priv->dot_clk)
278 clk_disable(priv->dot_clk);
279 }
280}
281
282static int sh_mobile_lcdc_sginit(struct fb_info *info, 272static int sh_mobile_lcdc_sginit(struct fb_info *info,
283 struct list_head *pagelist) 273 struct list_head *pagelist)
284{ 274{
285 struct sh_mobile_lcdc_chan *ch = info->par; 275 struct sh_mobile_lcdc_chan *ch = info->par;
286 unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT; 276 unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
287 struct page *page; 277 struct page *page;
288 int nr_pages = 0; 278 int nr_pages = 0;
289 279
@@ -299,7 +289,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
299 struct list_head *pagelist) 289 struct list_head *pagelist)
300{ 290{
301 struct sh_mobile_lcdc_chan *ch = info->par; 291 struct sh_mobile_lcdc_chan *ch = info->par;
302 struct sh_mobile_lcdc_board_cfg *bcfg = &ch->cfg.board_cfg; 292 const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
303 293
304 /* enable clocks before accessing hardware */ 294 /* enable clocks before accessing hardware */
305 sh_mobile_lcdc_clk_on(ch->lcdc); 295 sh_mobile_lcdc_clk_on(ch->lcdc);
@@ -323,16 +313,15 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
323 unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); 313 unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
324 314
325 /* trigger panel update */ 315 /* trigger panel update */
326 dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 316 dma_map_sg(ch->lcdc->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
327 if (bcfg->start_transfer) 317 if (panel->start_transfer)
328 bcfg->start_transfer(bcfg->board_data, ch, 318 panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops);
329 &sh_mobile_lcdc_sys_bus_ops);
330 lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); 319 lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
331 dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 320 dma_unmap_sg(ch->lcdc->dev, ch->sglist, nr_pages,
321 DMA_TO_DEVICE);
332 } else { 322 } else {
333 if (bcfg->start_transfer) 323 if (panel->start_transfer)
334 bcfg->start_transfer(bcfg->board_data, ch, 324 panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops);
335 &sh_mobile_lcdc_sys_bus_ops);
336 lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); 325 lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
337 } 326 }
338} 327}
@@ -345,6 +334,217 @@ static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
345 schedule_delayed_work(&info->deferred_work, fbdefio->delay); 334 schedule_delayed_work(&info->deferred_work, fbdefio->delay);
346} 335}
347 336
337static void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch)
338{
339 const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
340
341 if (ch->tx_dev) {
342 int ret;
343
344 ret = ch->tx_dev->ops->display_on(ch->tx_dev);
345 if (ret < 0)
346 return;
347
348 if (ret == SH_MOBILE_LCDC_DISPLAY_DISCONNECTED)
349 ch->info->state = FBINFO_STATE_SUSPENDED;
350 }
351
352 /* HDMI must be enabled before LCDC configuration */
353 if (panel->display_on)
354 panel->display_on();
355}
356
357static void sh_mobile_lcdc_display_off(struct sh_mobile_lcdc_chan *ch)
358{
359 const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
360
361 if (panel->display_off)
362 panel->display_off();
363
364 if (ch->tx_dev)
365 ch->tx_dev->ops->display_off(ch->tx_dev);
366}
367
368static bool
369sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch,
370 const struct fb_videomode *new_mode)
371{
372 dev_dbg(ch->info->dev, "Old %ux%u, new %ux%u\n",
373 ch->display.mode.xres, ch->display.mode.yres,
374 new_mode->xres, new_mode->yres);
375
376 /* It can be a different monitor with an equal video-mode */
377 if (fb_mode_is_equal(&ch->display.mode, new_mode))
378 return false;
379
380 dev_dbg(ch->info->dev, "Switching %u -> %u lines\n",
381 ch->display.mode.yres, new_mode->yres);
382 ch->display.mode = *new_mode;
383
384 return true;
385}
386
387static int sh_mobile_check_var(struct fb_var_screeninfo *var,
388 struct fb_info *info);
389
390static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
391 enum sh_mobile_lcdc_entity_event event,
392 const struct fb_videomode *mode,
393 const struct fb_monspecs *monspec)
394{
395 struct fb_info *info = ch->info;
396 struct fb_var_screeninfo var;
397 int ret = 0;
398
399 switch (event) {
400 case SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT:
401 /* HDMI plug in */
402 if (lock_fb_info(info)) {
403 console_lock();
404
405 ch->display.width = monspec->max_x * 10;
406 ch->display.height = monspec->max_y * 10;
407
408 if (!sh_mobile_lcdc_must_reconfigure(ch, mode) &&
409 info->state == FBINFO_STATE_RUNNING) {
410 /* First activation with the default monitor.
411 * Just turn on, if we run a resume here, the
412 * logo disappears.
413 */
414 info->var.width = monspec->max_x * 10;
415 info->var.height = monspec->max_y * 10;
416 sh_mobile_lcdc_display_on(ch);
417 } else {
418 /* New monitor or have to wake up */
419 fb_set_suspend(info, 0);
420 }
421
422 console_unlock();
423 unlock_fb_info(info);
424 }
425 break;
426
427 case SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT:
428 /* HDMI disconnect */
429 if (lock_fb_info(info)) {
430 console_lock();
431 fb_set_suspend(info, 1);
432 console_unlock();
433 unlock_fb_info(info);
434 }
435 break;
436
437 case SH_MOBILE_LCDC_EVENT_DISPLAY_MODE:
438 /* Validate a proposed new mode */
439 fb_videomode_to_var(&var, mode);
440 var.bits_per_pixel = info->var.bits_per_pixel;
441 var.grayscale = info->var.grayscale;
442 ret = sh_mobile_check_var(&var, info);
443 break;
444 }
445
446 return ret;
447}
448
449/* -----------------------------------------------------------------------------
450 * Format helpers
451 */
452
453struct sh_mobile_lcdc_format_info {
454 u32 fourcc;
455 unsigned int bpp;
456 bool yuv;
457 u32 lddfr;
458};
459
460static const struct sh_mobile_lcdc_format_info sh_mobile_format_infos[] = {
461 {
462 .fourcc = V4L2_PIX_FMT_RGB565,
463 .bpp = 16,
464 .yuv = false,
465 .lddfr = LDDFR_PKF_RGB16,
466 }, {
467 .fourcc = V4L2_PIX_FMT_BGR24,
468 .bpp = 24,
469 .yuv = false,
470 .lddfr = LDDFR_PKF_RGB24,
471 }, {
472 .fourcc = V4L2_PIX_FMT_BGR32,
473 .bpp = 32,
474 .yuv = false,
475 .lddfr = LDDFR_PKF_ARGB32,
476 }, {
477 .fourcc = V4L2_PIX_FMT_NV12,
478 .bpp = 12,
479 .yuv = true,
480 .lddfr = LDDFR_CC | LDDFR_YF_420,
481 }, {
482 .fourcc = V4L2_PIX_FMT_NV21,
483 .bpp = 12,
484 .yuv = true,
485 .lddfr = LDDFR_CC | LDDFR_YF_420,
486 }, {
487 .fourcc = V4L2_PIX_FMT_NV16,
488 .bpp = 16,
489 .yuv = true,
490 .lddfr = LDDFR_CC | LDDFR_YF_422,
491 }, {
492 .fourcc = V4L2_PIX_FMT_NV61,
493 .bpp = 16,
494 .yuv = true,
495 .lddfr = LDDFR_CC | LDDFR_YF_422,
496 }, {
497 .fourcc = V4L2_PIX_FMT_NV24,
498 .bpp = 24,
499 .yuv = true,
500 .lddfr = LDDFR_CC | LDDFR_YF_444,
501 }, {
502 .fourcc = V4L2_PIX_FMT_NV42,
503 .bpp = 24,
504 .yuv = true,
505 .lddfr = LDDFR_CC | LDDFR_YF_444,
506 },
507};
508
509static const struct sh_mobile_lcdc_format_info *
510sh_mobile_format_info(u32 fourcc)
511{
512 unsigned int i;
513
514 for (i = 0; i < ARRAY_SIZE(sh_mobile_format_infos); ++i) {
515 if (sh_mobile_format_infos[i].fourcc == fourcc)
516 return &sh_mobile_format_infos[i];
517 }
518
519 return NULL;
520}
521
522static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var)
523{
524 if (var->grayscale > 1)
525 return var->grayscale;
526
527 switch (var->bits_per_pixel) {
528 case 16:
529 return V4L2_PIX_FMT_RGB565;
530 case 24:
531 return V4L2_PIX_FMT_BGR24;
532 case 32:
533 return V4L2_PIX_FMT_BGR32;
534 default:
535 return 0;
536 }
537}
538
539static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var)
540{
541 return var->grayscale > 1;
542}
543
544/* -----------------------------------------------------------------------------
545 * Start, stop and IRQ
546 */
547
348static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) 548static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
349{ 549{
350 struct sh_mobile_lcdc_priv *priv = data; 550 struct sh_mobile_lcdc_priv *priv = data;
@@ -385,6 +585,26 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
385 return IRQ_HANDLED; 585 return IRQ_HANDLED;
386} 586}
387 587
588static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
589{
590 unsigned long ldintr;
591 int ret;
592
593 /* Enable VSync End interrupt and be careful not to acknowledge any
594 * pending interrupt.
595 */
596 ldintr = lcdc_read(ch->lcdc, _LDINTR);
597 ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK;
598 lcdc_write(ch->lcdc, _LDINTR, ldintr);
599
600 ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
601 msecs_to_jiffies(100));
602 if (!ret)
603 return -ETIMEDOUT;
604
605 return 0;
606}
607
388static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, 608static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
389 int start) 609 int start)
390{ 610{
@@ -416,53 +636,52 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
416 636
417static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) 637static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
418{ 638{
419 struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var; 639 const struct fb_var_screeninfo *var = &ch->info->var;
640 const struct fb_videomode *mode = &ch->display.mode;
420 unsigned long h_total, hsync_pos, display_h_total; 641 unsigned long h_total, hsync_pos, display_h_total;
421 u32 tmp; 642 u32 tmp;
422 643
423 tmp = ch->ldmt1r_value; 644 tmp = ch->ldmt1r_value;
424 tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL; 645 tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL;
425 tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL; 646 tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL;
426 tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0; 647 tmp |= (ch->cfg->flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
427 tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0; 648 tmp |= (ch->cfg->flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
428 tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0; 649 tmp |= (ch->cfg->flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
429 tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0; 650 tmp |= (ch->cfg->flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
430 tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0; 651 tmp |= (ch->cfg->flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
431 lcdc_write_chan(ch, LDMT1R, tmp); 652 lcdc_write_chan(ch, LDMT1R, tmp);
432 653
433 /* setup SYS bus */ 654 /* setup SYS bus */
434 lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); 655 lcdc_write_chan(ch, LDMT2R, ch->cfg->sys_bus_cfg.ldmt2r);
435 lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); 656 lcdc_write_chan(ch, LDMT3R, ch->cfg->sys_bus_cfg.ldmt3r);
436 657
437 /* horizontal configuration */ 658 /* horizontal configuration */
438 h_total = display_var->xres + display_var->hsync_len + 659 h_total = mode->xres + mode->hsync_len + mode->left_margin
439 display_var->left_margin + display_var->right_margin; 660 + mode->right_margin;
440 tmp = h_total / 8; /* HTCN */ 661 tmp = h_total / 8; /* HTCN */
441 tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */ 662 tmp |= (min(mode->xres, ch->xres) / 8) << 16; /* HDCN */
442 lcdc_write_chan(ch, LDHCNR, tmp); 663 lcdc_write_chan(ch, LDHCNR, tmp);
443 664
444 hsync_pos = display_var->xres + display_var->right_margin; 665 hsync_pos = mode->xres + mode->right_margin;
445 tmp = hsync_pos / 8; /* HSYNP */ 666 tmp = hsync_pos / 8; /* HSYNP */
446 tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */ 667 tmp |= (mode->hsync_len / 8) << 16; /* HSYNW */
447 lcdc_write_chan(ch, LDHSYNR, tmp); 668 lcdc_write_chan(ch, LDHSYNR, tmp);
448 669
449 /* vertical configuration */ 670 /* vertical configuration */
450 tmp = display_var->yres + display_var->vsync_len + 671 tmp = mode->yres + mode->vsync_len + mode->upper_margin
451 display_var->upper_margin + display_var->lower_margin; /* VTLN */ 672 + mode->lower_margin; /* VTLN */
452 tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */ 673 tmp |= min(mode->yres, ch->yres) << 16; /* VDLN */
453 lcdc_write_chan(ch, LDVLNR, tmp); 674 lcdc_write_chan(ch, LDVLNR, tmp);
454 675
455 tmp = display_var->yres + display_var->lower_margin; /* VSYNP */ 676 tmp = mode->yres + mode->lower_margin; /* VSYNP */
456 tmp |= display_var->vsync_len << 16; /* VSYNW */ 677 tmp |= mode->vsync_len << 16; /* VSYNW */
457 lcdc_write_chan(ch, LDVSYNR, tmp); 678 lcdc_write_chan(ch, LDVSYNR, tmp);
458 679
459 /* Adjust horizontal synchronisation for HDMI */ 680 /* Adjust horizontal synchronisation for HDMI */
460 display_h_total = display_var->xres + display_var->hsync_len + 681 display_h_total = mode->xres + mode->hsync_len + mode->left_margin
461 display_var->left_margin + display_var->right_margin; 682 + mode->right_margin;
462 tmp = ((display_var->xres & 7) << 24) | 683 tmp = ((mode->xres & 7) << 24) | ((display_h_total & 7) << 16)
463 ((display_h_total & 7) << 16) | 684 | ((mode->hsync_len & 7) << 8) | (hsync_pos & 7);
464 ((display_var->hsync_len & 7) << 8) |
465 (hsync_pos & 7);
466 lcdc_write_chan(ch, LDHAJR, tmp); 685 lcdc_write_chan(ch, LDHAJR, tmp);
467} 686}
468 687
@@ -498,7 +717,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
498 /* Power supply */ 717 /* Power supply */
499 lcdc_write_chan(ch, LDPMR, 0); 718 lcdc_write_chan(ch, LDPMR, 0);
500 719
501 m = ch->cfg.clock_divider; 720 m = ch->cfg->clock_divider;
502 if (!m) 721 if (!m)
503 continue; 722 continue;
504 723
@@ -525,32 +744,10 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
525 744
526 sh_mobile_lcdc_geometry(ch); 745 sh_mobile_lcdc_geometry(ch);
527 746
528 switch (sh_mobile_format_fourcc(&ch->info->var)) { 747 tmp = ch->format->lddfr;
529 case V4L2_PIX_FMT_RGB565:
530 tmp = LDDFR_PKF_RGB16;
531 break;
532 case V4L2_PIX_FMT_BGR24:
533 tmp = LDDFR_PKF_RGB24;
534 break;
535 case V4L2_PIX_FMT_BGR32:
536 tmp = LDDFR_PKF_ARGB32;
537 break;
538 case V4L2_PIX_FMT_NV12:
539 case V4L2_PIX_FMT_NV21:
540 tmp = LDDFR_CC | LDDFR_YF_420;
541 break;
542 case V4L2_PIX_FMT_NV16:
543 case V4L2_PIX_FMT_NV61:
544 tmp = LDDFR_CC | LDDFR_YF_422;
545 break;
546 case V4L2_PIX_FMT_NV24:
547 case V4L2_PIX_FMT_NV42:
548 tmp = LDDFR_CC | LDDFR_YF_444;
549 break;
550 }
551 748
552 if (sh_mobile_format_is_yuv(&ch->info->var)) { 749 if (ch->format->yuv) {
553 switch (ch->info->var.colorspace) { 750 switch (ch->colorspace) {
554 case V4L2_COLORSPACE_REC709: 751 case V4L2_COLORSPACE_REC709:
555 tmp |= LDDFR_CF1; 752 tmp |= LDDFR_CF1;
556 break; 753 break;
@@ -563,7 +760,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
563 lcdc_write_chan(ch, LDDFR, tmp); 760 lcdc_write_chan(ch, LDDFR, tmp);
564 lcdc_write_chan(ch, LDMLSR, ch->pitch); 761 lcdc_write_chan(ch, LDMLSR, ch->pitch);
565 lcdc_write_chan(ch, LDSA1R, ch->base_addr_y); 762 lcdc_write_chan(ch, LDSA1R, ch->base_addr_y);
566 if (sh_mobile_format_is_yuv(&ch->info->var)) 763 if (ch->format->yuv)
567 lcdc_write_chan(ch, LDSA2R, ch->base_addr_c); 764 lcdc_write_chan(ch, LDSA2R, ch->base_addr_c);
568 765
569 /* When using deferred I/O mode, configure the LCDC for one-shot 766 /* When using deferred I/O mode, configure the LCDC for one-shot
@@ -571,7 +768,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
571 * continuous read mode. 768 * continuous read mode.
572 */ 769 */
573 if (ch->ldmt1r_value & LDMT1R_IFM && 770 if (ch->ldmt1r_value & LDMT1R_IFM &&
574 ch->cfg.sys_bus_cfg.deferred_io_msec) { 771 ch->cfg->sys_bus_cfg.deferred_io_msec) {
575 lcdc_write_chan(ch, LDSM1R, LDSM1R_OS); 772 lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
576 lcdc_write(priv, _LDINTR, LDINTR_FE); 773 lcdc_write(priv, _LDINTR, LDINTR_FE);
577 } else { 774 } else {
@@ -580,7 +777,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
580 } 777 }
581 778
582 /* Word and long word swap. */ 779 /* Word and long word swap. */
583 switch (sh_mobile_format_fourcc(&priv->ch[0].info->var)) { 780 switch (priv->ch[0].format->fourcc) {
584 case V4L2_PIX_FMT_RGB565: 781 case V4L2_PIX_FMT_RGB565:
585 case V4L2_PIX_FMT_NV21: 782 case V4L2_PIX_FMT_NV21:
586 case V4L2_PIX_FMT_NV61: 783 case V4L2_PIX_FMT_NV61:
@@ -609,7 +806,6 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
609static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) 806static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
610{ 807{
611 struct sh_mobile_meram_info *mdev = priv->meram_dev; 808 struct sh_mobile_meram_info *mdev = priv->meram_dev;
612 struct sh_mobile_lcdc_board_cfg *board_cfg;
613 struct sh_mobile_lcdc_chan *ch; 809 struct sh_mobile_lcdc_chan *ch;
614 unsigned long tmp; 810 unsigned long tmp;
615 int ret; 811 int ret;
@@ -626,15 +822,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
626 lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0); 822 lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
627 823
628 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 824 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
629 ch = &priv->ch[k]; 825 const struct sh_mobile_lcdc_panel_cfg *panel;
630 826
827 ch = &priv->ch[k];
631 if (!ch->enabled) 828 if (!ch->enabled)
632 continue; 829 continue;
633 830
634 board_cfg = &ch->cfg.board_cfg; 831 panel = &ch->cfg->panel_cfg;
635 if (board_cfg->setup_sys) { 832 if (panel->setup_sys) {
636 ret = board_cfg->setup_sys(board_cfg->board_data, ch, 833 ret = panel->setup_sys(ch, &sh_mobile_lcdc_sys_bus_ops);
637 &sh_mobile_lcdc_sys_bus_ops);
638 if (ret) 834 if (ret)
639 return ret; 835 return ret;
640 } 836 }
@@ -642,33 +838,30 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
642 838
643 /* Compute frame buffer base address and pitch for each channel. */ 839 /* Compute frame buffer base address and pitch for each channel. */
644 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 840 for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
645 struct sh_mobile_meram_cfg *cfg;
646 int pixelformat; 841 int pixelformat;
842 void *meram;
647 843
648 ch = &priv->ch[k]; 844 ch = &priv->ch[k];
649 if (!ch->enabled) 845 if (!ch->enabled)
650 continue; 846 continue;
651 847
652 ch->base_addr_y = ch->info->fix.smem_start; 848 ch->base_addr_y = ch->dma_handle;
653 ch->base_addr_c = ch->base_addr_y 849 ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual;
654 + ch->info->var.xres
655 * ch->info->var.yres_virtual;
656 ch->pitch = ch->info->fix.line_length;
657 850
658 /* Enable MERAM if possible. */ 851 /* Enable MERAM if possible. */
659 cfg = ch->cfg.meram_cfg; 852 if (mdev == NULL || mdev->ops == NULL ||
660 if (mdev == NULL || mdev->ops == NULL || cfg == NULL) 853 ch->cfg->meram_cfg == NULL)
661 continue; 854 continue;
662 855
663 /* we need to de-init configured ICBs before we can 856 /* we need to de-init configured ICBs before we can
664 * re-initialize them. 857 * re-initialize them.
665 */ 858 */
666 if (ch->meram_enabled) { 859 if (ch->meram) {
667 mdev->ops->meram_unregister(mdev, cfg); 860 mdev->ops->meram_unregister(mdev, ch->meram);
668 ch->meram_enabled = 0; 861 ch->meram = NULL;
669 } 862 }
670 863
671 switch (sh_mobile_format_fourcc(&ch->info->var)) { 864 switch (ch->format->fourcc) {
672 case V4L2_PIX_FMT_NV12: 865 case V4L2_PIX_FMT_NV12:
673 case V4L2_PIX_FMT_NV21: 866 case V4L2_PIX_FMT_NV21:
674 case V4L2_PIX_FMT_NV16: 867 case V4L2_PIX_FMT_NV16:
@@ -687,13 +880,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
687 break; 880 break;
688 } 881 }
689 882
690 ret = mdev->ops->meram_register(mdev, cfg, ch->pitch, 883 meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
691 ch->info->var.yres, pixelformat, 884 ch->pitch, ch->yres, pixelformat,
692 ch->base_addr_y, ch->base_addr_c,
693 &ch->base_addr_y, &ch->base_addr_c,
694 &ch->pitch); 885 &ch->pitch);
695 if (!ret) 886 if (!IS_ERR(meram)) {
696 ch->meram_enabled = 1; 887 mdev->ops->meram_update(mdev, meram,
888 ch->base_addr_y, ch->base_addr_c,
889 &ch->base_addr_y, &ch->base_addr_c);
890 ch->meram = meram;
891 }
697 } 892 }
698 893
699 /* Start the LCDC. */ 894 /* Start the LCDC. */
@@ -707,7 +902,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
707 if (!ch->enabled) 902 if (!ch->enabled)
708 continue; 903 continue;
709 904
710 tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; 905 tmp = ch->cfg->sys_bus_cfg.deferred_io_msec;
711 if (ch->ldmt1r_value & LDMT1R_IFM && tmp) { 906 if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
712 ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; 907 ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
713 ch->defio.delay = msecs_to_jiffies(tmp); 908 ch->defio.delay = msecs_to_jiffies(tmp);
@@ -715,11 +910,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
715 fb_deferred_io_init(ch->info); 910 fb_deferred_io_init(ch->info);
716 } 911 }
717 912
718 board_cfg = &ch->cfg.board_cfg; 913 sh_mobile_lcdc_display_on(ch);
719 if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
720 board_cfg->display_on(board_cfg->board_data, ch->info);
721 module_put(board_cfg->owner);
722 }
723 914
724 if (ch->bl) { 915 if (ch->bl) {
725 ch->bl->props.power = FB_BLANK_UNBLANK; 916 ch->bl->props.power = FB_BLANK_UNBLANK;
@@ -733,7 +924,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
733static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) 924static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
734{ 925{
735 struct sh_mobile_lcdc_chan *ch; 926 struct sh_mobile_lcdc_chan *ch;
736 struct sh_mobile_lcdc_board_cfg *board_cfg;
737 int k; 927 int k;
738 928
739 /* clean up deferred io and ask board code to disable panel */ 929 /* clean up deferred io and ask board code to disable panel */
@@ -760,20 +950,14 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
760 backlight_update_status(ch->bl); 950 backlight_update_status(ch->bl);
761 } 951 }
762 952
763 board_cfg = &ch->cfg.board_cfg; 953 sh_mobile_lcdc_display_off(ch);
764 if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
765 board_cfg->display_off(board_cfg->board_data);
766 module_put(board_cfg->owner);
767 }
768 954
769 /* disable the meram */ 955 /* disable the meram */
770 if (ch->meram_enabled) { 956 if (ch->meram) {
771 struct sh_mobile_meram_cfg *cfg;
772 struct sh_mobile_meram_info *mdev; 957 struct sh_mobile_meram_info *mdev;
773 cfg = ch->cfg.meram_cfg;
774 mdev = priv->meram_dev; 958 mdev = priv->meram_dev;
775 mdev->ops->meram_unregister(mdev, cfg); 959 mdev->ops->meram_unregister(mdev, ch->meram);
776 ch->meram_enabled = 0; 960 ch->meram = 0;
777 } 961 }
778 962
779 } 963 }
@@ -790,86 +974,9 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
790 sh_mobile_lcdc_clk_off(priv); 974 sh_mobile_lcdc_clk_off(priv);
791} 975}
792 976
793static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) 977/* -----------------------------------------------------------------------------
794{ 978 * Frame buffer operations
795 int interface_type = ch->cfg.interface_type; 979 */
796
797 switch (interface_type) {
798 case RGB8:
799 case RGB9:
800 case RGB12A:
801 case RGB12B:
802 case RGB16:
803 case RGB18:
804 case RGB24:
805 case SYS8A:
806 case SYS8B:
807 case SYS8C:
808 case SYS8D:
809 case SYS9:
810 case SYS12:
811 case SYS16A:
812 case SYS16B:
813 case SYS16C:
814 case SYS18:
815 case SYS24:
816 break;
817 default:
818 return -EINVAL;
819 }
820
821 /* SUBLCD only supports SYS interface */
822 if (lcdc_chan_is_sublcd(ch)) {
823 if (!(interface_type & LDMT1R_IFM))
824 return -EINVAL;
825
826 interface_type &= ~LDMT1R_IFM;
827 }
828
829 ch->ldmt1r_value = interface_type;
830 return 0;
831}
832
833static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
834 int clock_source,
835 struct sh_mobile_lcdc_priv *priv)
836{
837 char *str;
838
839 switch (clock_source) {
840 case LCDC_CLK_BUS:
841 str = "bus_clk";
842 priv->lddckr = LDDCKR_ICKSEL_BUS;
843 break;
844 case LCDC_CLK_PERIPHERAL:
845 str = "peripheral_clk";
846 priv->lddckr = LDDCKR_ICKSEL_MIPI;
847 break;
848 case LCDC_CLK_EXTERNAL:
849 str = NULL;
850 priv->lddckr = LDDCKR_ICKSEL_HDMI;
851 break;
852 default:
853 return -EINVAL;
854 }
855
856 if (str) {
857 priv->dot_clk = clk_get(&pdev->dev, str);
858 if (IS_ERR(priv->dot_clk)) {
859 dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
860 return PTR_ERR(priv->dot_clk);
861 }
862 }
863
864 /* Runtime PM support involves two step for this driver:
865 * 1) Enable Runtime PM
866 * 2) Force Runtime PM Resume since hardware is accessed from probe()
867 */
868 priv->dev = &pdev->dev;
869 pm_runtime_enable(priv->dev);
870 pm_runtime_resume(priv->dev);
871 return 0;
872}
873 980
874static int sh_mobile_lcdc_setcolreg(u_int regno, 981static int sh_mobile_lcdc_setcolreg(u_int regno,
875 u_int red, u_int green, u_int blue, 982 u_int red, u_int green, u_int blue,
@@ -936,14 +1043,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
936 unsigned long new_pan_offset; 1043 unsigned long new_pan_offset;
937 unsigned long base_addr_y, base_addr_c; 1044 unsigned long base_addr_y, base_addr_c;
938 unsigned long c_offset; 1045 unsigned long c_offset;
939 bool yuv = sh_mobile_format_is_yuv(&info->var);
940 1046
941 if (!yuv) 1047 if (!ch->format->yuv)
942 new_pan_offset = var->yoffset * info->fix.line_length 1048 new_pan_offset = var->yoffset * ch->pitch
943 + var->xoffset * (info->var.bits_per_pixel / 8); 1049 + var->xoffset * (ch->format->bpp / 8);
944 else 1050 else
945 new_pan_offset = var->yoffset * info->fix.line_length 1051 new_pan_offset = var->yoffset * ch->pitch + var->xoffset;
946 + var->xoffset;
947 1052
948 if (new_pan_offset == ch->pan_offset) 1053 if (new_pan_offset == ch->pan_offset)
949 return 0; /* No change, do nothing */ 1054 return 0; /* No change, do nothing */
@@ -952,39 +1057,33 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
952 1057
953 /* Set the source address for the next refresh */ 1058 /* Set the source address for the next refresh */
954 base_addr_y = ch->dma_handle + new_pan_offset; 1059 base_addr_y = ch->dma_handle + new_pan_offset;
955 if (yuv) { 1060 if (ch->format->yuv) {
956 /* Set y offset */ 1061 /* Set y offset */
957 c_offset = var->yoffset * info->fix.line_length 1062 c_offset = var->yoffset * ch->pitch
958 * (info->var.bits_per_pixel - 8) / 8; 1063 * (ch->format->bpp - 8) / 8;
959 base_addr_c = ch->dma_handle 1064 base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual
960 + info->var.xres * info->var.yres_virtual
961 + c_offset; 1065 + c_offset;
962 /* Set x offset */ 1066 /* Set x offset */
963 if (sh_mobile_format_fourcc(&info->var) == V4L2_PIX_FMT_NV24) 1067 if (ch->format->fourcc == V4L2_PIX_FMT_NV24)
964 base_addr_c += 2 * var->xoffset; 1068 base_addr_c += 2 * var->xoffset;
965 else 1069 else
966 base_addr_c += var->xoffset; 1070 base_addr_c += var->xoffset;
967 } 1071 }
968 1072
969 if (ch->meram_enabled) { 1073 if (ch->meram) {
970 struct sh_mobile_meram_cfg *cfg;
971 struct sh_mobile_meram_info *mdev; 1074 struct sh_mobile_meram_info *mdev;
972 int ret;
973 1075
974 cfg = ch->cfg.meram_cfg;
975 mdev = priv->meram_dev; 1076 mdev = priv->meram_dev;
976 ret = mdev->ops->meram_update(mdev, cfg, 1077 mdev->ops->meram_update(mdev, ch->meram,
977 base_addr_y, base_addr_c, 1078 base_addr_y, base_addr_c,
978 &base_addr_y, &base_addr_c); 1079 &base_addr_y, &base_addr_c);
979 if (ret)
980 return ret;
981 } 1080 }
982 1081
983 ch->base_addr_y = base_addr_y; 1082 ch->base_addr_y = base_addr_y;
984 ch->base_addr_c = base_addr_c; 1083 ch->base_addr_c = base_addr_c;
985 1084
986 lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); 1085 lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
987 if (yuv) 1086 if (ch->format->yuv)
988 lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); 1087 lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
989 1088
990 if (lcdc_chan_is_sublcd(ch)) 1089 if (lcdc_chan_is_sublcd(ch))
@@ -999,27 +1098,6 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
999 return 0; 1098 return 0;
1000} 1099}
1001 1100
1002static int sh_mobile_wait_for_vsync(struct fb_info *info)
1003{
1004 struct sh_mobile_lcdc_chan *ch = info->par;
1005 unsigned long ldintr;
1006 int ret;
1007
1008 /* Enable VSync End interrupt and be careful not to acknowledge any
1009 * pending interrupt.
1010 */
1011 ldintr = lcdc_read(ch->lcdc, _LDINTR);
1012 ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK;
1013 lcdc_write(ch->lcdc, _LDINTR, ldintr);
1014
1015 ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
1016 msecs_to_jiffies(100));
1017 if (!ret)
1018 return -ETIMEDOUT;
1019
1020 return 0;
1021}
1022
1023static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, 1101static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
1024 unsigned long arg) 1102 unsigned long arg)
1025{ 1103{
@@ -1027,7 +1105,7 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
1027 1105
1028 switch (cmd) { 1106 switch (cmd) {
1029 case FBIO_WAITFORVSYNC: 1107 case FBIO_WAITFORVSYNC:
1030 retval = sh_mobile_wait_for_vsync(info); 1108 retval = sh_mobile_wait_for_vsync(info->par);
1031 break; 1109 break;
1032 1110
1033 default: 1111 default:
@@ -1040,7 +1118,8 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
1040static void sh_mobile_fb_reconfig(struct fb_info *info) 1118static void sh_mobile_fb_reconfig(struct fb_info *info)
1041{ 1119{
1042 struct sh_mobile_lcdc_chan *ch = info->par; 1120 struct sh_mobile_lcdc_chan *ch = info->par;
1043 struct fb_videomode mode1, mode2; 1121 struct fb_var_screeninfo var;
1122 struct fb_videomode mode;
1044 struct fb_event event; 1123 struct fb_event event;
1045 int evnt = FB_EVENT_MODE_CHANGE_ALL; 1124 int evnt = FB_EVENT_MODE_CHANGE_ALL;
1046 1125
@@ -1048,14 +1127,19 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
1048 /* More framebuffer users are active */ 1127 /* More framebuffer users are active */
1049 return; 1128 return;
1050 1129
1051 fb_var_to_videomode(&mode1, &ch->display_var); 1130 fb_var_to_videomode(&mode, &info->var);
1052 fb_var_to_videomode(&mode2, &info->var);
1053 1131
1054 if (fb_mode_is_equal(&mode1, &mode2)) 1132 if (fb_mode_is_equal(&ch->display.mode, &mode))
1055 return; 1133 return;
1056 1134
1057 /* Display has been re-plugged, framebuffer is free now, reconfigure */ 1135 /* Display has been re-plugged, framebuffer is free now, reconfigure */
1058 if (fb_set_var(info, &ch->display_var) < 0) 1136 var = info->var;
1137 fb_videomode_to_var(&var, &ch->display.mode);
1138 var.width = ch->display.width;
1139 var.height = ch->display.height;
1140 var.activate = FB_ACTIVATE_NOW;
1141
1142 if (fb_set_var(info, &var) < 0)
1059 /* Couldn't reconfigure, hopefully, can continue as before */ 1143 /* Couldn't reconfigure, hopefully, can continue as before */
1060 return; 1144 return;
1061 1145
@@ -1065,7 +1149,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
1065 * user event, we have to call the chain ourselves. 1149 * user event, we have to call the chain ourselves.
1066 */ 1150 */
1067 event.info = info; 1151 event.info = info;
1068 event.data = &mode1; 1152 event.data = &ch->display.mode;
1069 fb_notifier_call_chain(evnt, &event); 1153 fb_notifier_call_chain(evnt, &event);
1070} 1154}
1071 1155
@@ -1124,8 +1208,8 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
1124 * distance between two modes is defined as the size of the 1208 * distance between two modes is defined as the size of the
1125 * non-overlapping parts of the two rectangles. 1209 * non-overlapping parts of the two rectangles.
1126 */ 1210 */
1127 for (i = 0; i < ch->cfg.num_cfg; ++i) { 1211 for (i = 0; i < ch->cfg->num_modes; ++i) {
1128 const struct fb_videomode *mode = &ch->cfg.lcd_cfg[i]; 1212 const struct fb_videomode *mode = &ch->cfg->lcd_modes[i];
1129 unsigned int dist; 1213 unsigned int dist;
1130 1214
1131 /* We can only round up. */ 1215 /* We can only round up. */
@@ -1144,7 +1228,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
1144 } 1228 }
1145 1229
1146 /* If no available mode can be used, return an error. */ 1230 /* If no available mode can be used, return an error. */
1147 if (ch->cfg.num_cfg != 0) { 1231 if (ch->cfg->num_modes != 0) {
1148 if (best_dist == (unsigned int)-1) 1232 if (best_dist == (unsigned int)-1)
1149 return -EINVAL; 1233 return -EINVAL;
1150 1234
@@ -1161,32 +1245,17 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
1161 var->yres_virtual = var->yres; 1245 var->yres_virtual = var->yres;
1162 1246
1163 if (sh_mobile_format_is_fourcc(var)) { 1247 if (sh_mobile_format_is_fourcc(var)) {
1164 switch (var->grayscale) { 1248 const struct sh_mobile_lcdc_format_info *format;
1165 case V4L2_PIX_FMT_NV12: 1249
1166 case V4L2_PIX_FMT_NV21: 1250 format = sh_mobile_format_info(var->grayscale);
1167 var->bits_per_pixel = 12; 1251 if (format == NULL)
1168 break;
1169 case V4L2_PIX_FMT_RGB565:
1170 case V4L2_PIX_FMT_NV16:
1171 case V4L2_PIX_FMT_NV61:
1172 var->bits_per_pixel = 16;
1173 break;
1174 case V4L2_PIX_FMT_BGR24:
1175 case V4L2_PIX_FMT_NV24:
1176 case V4L2_PIX_FMT_NV42:
1177 var->bits_per_pixel = 24;
1178 break;
1179 case V4L2_PIX_FMT_BGR32:
1180 var->bits_per_pixel = 32;
1181 break;
1182 default:
1183 return -EINVAL; 1252 return -EINVAL;
1184 } 1253 var->bits_per_pixel = format->bpp;
1185 1254
1186 /* Default to RGB and JPEG color-spaces for RGB and YUV formats 1255 /* Default to RGB and JPEG color-spaces for RGB and YUV formats
1187 * respectively. 1256 * respectively.
1188 */ 1257 */
1189 if (!sh_mobile_format_is_yuv(var)) 1258 if (!format->yuv)
1190 var->colorspace = V4L2_COLORSPACE_SRGB; 1259 var->colorspace = V4L2_COLORSPACE_SRGB;
1191 else if (var->colorspace != V4L2_COLORSPACE_REC709) 1260 else if (var->colorspace != V4L2_COLORSPACE_REC709)
1192 var->colorspace = V4L2_COLORSPACE_JPEG; 1261 var->colorspace = V4L2_COLORSPACE_JPEG;
@@ -1246,22 +1315,28 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
1246static int sh_mobile_set_par(struct fb_info *info) 1315static int sh_mobile_set_par(struct fb_info *info)
1247{ 1316{
1248 struct sh_mobile_lcdc_chan *ch = info->par; 1317 struct sh_mobile_lcdc_chan *ch = info->par;
1249 u32 line_length = info->fix.line_length;
1250 int ret; 1318 int ret;
1251 1319
1252 sh_mobile_lcdc_stop(ch->lcdc); 1320 sh_mobile_lcdc_stop(ch->lcdc);
1253 1321
1254 if (sh_mobile_format_is_yuv(&info->var)) 1322 ch->format = sh_mobile_format_info(sh_mobile_format_fourcc(&info->var));
1255 info->fix.line_length = info->var.xres; 1323 ch->colorspace = info->var.colorspace;
1324
1325 ch->xres = info->var.xres;
1326 ch->xres_virtual = info->var.xres_virtual;
1327 ch->yres = info->var.yres;
1328 ch->yres_virtual = info->var.yres_virtual;
1329
1330 if (ch->format->yuv)
1331 ch->pitch = info->var.xres;
1256 else 1332 else
1257 info->fix.line_length = info->var.xres 1333 ch->pitch = info->var.xres * ch->format->bpp / 8;
1258 * info->var.bits_per_pixel / 8;
1259 1334
1260 ret = sh_mobile_lcdc_start(ch->lcdc); 1335 ret = sh_mobile_lcdc_start(ch->lcdc);
1261 if (ret < 0) { 1336 if (ret < 0)
1262 dev_err(info->dev, "%s: unable to restart LCDC\n", __func__); 1337 dev_err(info->dev, "%s: unable to restart LCDC\n", __func__);
1263 info->fix.line_length = line_length; 1338
1264 } 1339 info->fix.line_length = ch->pitch;
1265 1340
1266 if (sh_mobile_format_is_fourcc(&info->var)) { 1341 if (sh_mobile_format_is_fourcc(&info->var)) {
1267 info->fix.type = FB_TYPE_FOURCC; 1342 info->fix.type = FB_TYPE_FOURCC;
@@ -1290,8 +1365,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
1290 /* blank the screen? */ 1365 /* blank the screen? */
1291 if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) { 1366 if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) {
1292 struct fb_fillrect rect = { 1367 struct fb_fillrect rect = {
1293 .width = info->var.xres, 1368 .width = ch->xres,
1294 .height = info->var.yres, 1369 .height = ch->yres,
1295 }; 1370 };
1296 sh_mobile_lcdc_fillrect(info, &rect); 1371 sh_mobile_lcdc_fillrect(info, &rect);
1297 } 1372 }
@@ -1307,8 +1382,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
1307 * mode will reenable the clocks and update the screen in time, 1382 * mode will reenable the clocks and update the screen in time,
1308 * so it does not need this. */ 1383 * so it does not need this. */
1309 if (!info->fbdefio) { 1384 if (!info->fbdefio) {
1310 sh_mobile_wait_for_vsync(info); 1385 sh_mobile_wait_for_vsync(ch);
1311 sh_mobile_wait_for_vsync(info); 1386 sh_mobile_wait_for_vsync(ch);
1312 } 1387 }
1313 sh_mobile_lcdc_clk_off(p); 1388 sh_mobile_lcdc_clk_off(p);
1314 } 1389 }
@@ -1334,25 +1409,161 @@ static struct fb_ops sh_mobile_lcdc_ops = {
1334 .fb_set_par = sh_mobile_set_par, 1409 .fb_set_par = sh_mobile_set_par,
1335}; 1410};
1336 1411
1412static void
1413sh_mobile_lcdc_channel_fb_unregister(struct sh_mobile_lcdc_chan *ch)
1414{
1415 if (ch->info && ch->info->dev)
1416 unregister_framebuffer(ch->info);
1417}
1418
1419static int __devinit
1420sh_mobile_lcdc_channel_fb_register(struct sh_mobile_lcdc_chan *ch)
1421{
1422 struct fb_info *info = ch->info;
1423 int ret;
1424
1425 if (info->fbdefio) {
1426 ch->sglist = vmalloc(sizeof(struct scatterlist) *
1427 ch->fb_size >> PAGE_SHIFT);
1428 if (!ch->sglist) {
1429 dev_err(ch->lcdc->dev, "cannot allocate sglist\n");
1430 return -ENOMEM;
1431 }
1432 }
1433
1434 info->bl_dev = ch->bl;
1435
1436 ret = register_framebuffer(info);
1437 if (ret < 0)
1438 return ret;
1439
1440 dev_info(ch->lcdc->dev, "registered %s/%s as %dx%d %dbpp.\n",
1441 dev_name(ch->lcdc->dev), (ch->cfg->chan == LCDC_CHAN_MAINLCD) ?
1442 "mainlcd" : "sublcd", info->var.xres, info->var.yres,
1443 info->var.bits_per_pixel);
1444
1445 /* deferred io mode: disable clock to save power */
1446 if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
1447 sh_mobile_lcdc_clk_off(ch->lcdc);
1448
1449 return ret;
1450}
1451
1452static void
1453sh_mobile_lcdc_channel_fb_cleanup(struct sh_mobile_lcdc_chan *ch)
1454{
1455 struct fb_info *info = ch->info;
1456
1457 if (!info || !info->device)
1458 return;
1459
1460 if (ch->sglist)
1461 vfree(ch->sglist);
1462
1463 fb_dealloc_cmap(&info->cmap);
1464 framebuffer_release(info);
1465}
1466
1467static int __devinit
1468sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
1469 const struct fb_videomode *mode,
1470 unsigned int num_modes)
1471{
1472 struct sh_mobile_lcdc_priv *priv = ch->lcdc;
1473 struct fb_var_screeninfo *var;
1474 struct fb_info *info;
1475 int ret;
1476
1477 /* Allocate and initialize the frame buffer device. Create the modes
1478 * list and allocate the color map.
1479 */
1480 info = framebuffer_alloc(0, priv->dev);
1481 if (info == NULL) {
1482 dev_err(priv->dev, "unable to allocate fb_info\n");
1483 return -ENOMEM;
1484 }
1485
1486 ch->info = info;
1487
1488 info->flags = FBINFO_FLAG_DEFAULT;
1489 info->fbops = &sh_mobile_lcdc_ops;
1490 info->device = priv->dev;
1491 info->screen_base = ch->fb_mem;
1492 info->pseudo_palette = &ch->pseudo_palette;
1493 info->par = ch;
1494
1495 fb_videomode_to_modelist(mode, num_modes, &info->modelist);
1496
1497 ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
1498 if (ret < 0) {
1499 dev_err(priv->dev, "unable to allocate cmap\n");
1500 return ret;
1501 }
1502
1503 /* Initialize fixed screen information. Restrict pan to 2 lines steps
1504 * for NV12 and NV21.
1505 */
1506 info->fix = sh_mobile_lcdc_fix;
1507 info->fix.smem_start = ch->dma_handle;
1508 info->fix.smem_len = ch->fb_size;
1509 info->fix.line_length = ch->pitch;
1510
1511 if (ch->format->yuv)
1512 info->fix.visual = FB_VISUAL_FOURCC;
1513 else
1514 info->fix.visual = FB_VISUAL_TRUECOLOR;
1515
1516 if (ch->format->fourcc == V4L2_PIX_FMT_NV12 ||
1517 ch->format->fourcc == V4L2_PIX_FMT_NV21)
1518 info->fix.ypanstep = 2;
1519
1520 /* Initialize variable screen information using the first mode as
1521 * default. The default Y virtual resolution is twice the panel size to
1522 * allow for double-buffering.
1523 */
1524 var = &info->var;
1525 fb_videomode_to_var(var, mode);
1526 var->width = ch->cfg->panel_cfg.width;
1527 var->height = ch->cfg->panel_cfg.height;
1528 var->yres_virtual = var->yres * 2;
1529 var->activate = FB_ACTIVATE_NOW;
1530
1531 /* Use the legacy API by default for RGB formats, and the FOURCC API
1532 * for YUV formats.
1533 */
1534 if (!ch->format->yuv)
1535 var->bits_per_pixel = ch->format->bpp;
1536 else
1537 var->grayscale = ch->format->fourcc;
1538
1539 ret = sh_mobile_check_var(var, info);
1540 if (ret)
1541 return ret;
1542
1543 return 0;
1544}
1545
1546/* -----------------------------------------------------------------------------
1547 * Backlight
1548 */
1549
1337static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev) 1550static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev)
1338{ 1551{
1339 struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); 1552 struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
1340 struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg;
1341 int brightness = bdev->props.brightness; 1553 int brightness = bdev->props.brightness;
1342 1554
1343 if (bdev->props.power != FB_BLANK_UNBLANK || 1555 if (bdev->props.power != FB_BLANK_UNBLANK ||
1344 bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) 1556 bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
1345 brightness = 0; 1557 brightness = 0;
1346 1558
1347 return cfg->set_brightness(cfg->board_data, brightness); 1559 return ch->cfg->bl_info.set_brightness(brightness);
1348} 1560}
1349 1561
1350static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev) 1562static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev)
1351{ 1563{
1352 struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); 1564 struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
1353 struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg;
1354 1565
1355 return cfg->get_brightness(cfg->board_data); 1566 return ch->cfg->bl_info.get_brightness();
1356} 1567}
1357 1568
1358static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev, 1569static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev,
@@ -1373,7 +1584,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
1373{ 1584{
1374 struct backlight_device *bl; 1585 struct backlight_device *bl;
1375 1586
1376 bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch, 1587 bl = backlight_device_register(ch->cfg->bl_info.name, parent, ch,
1377 &sh_mobile_lcdc_bl_ops, NULL); 1588 &sh_mobile_lcdc_bl_ops, NULL);
1378 if (IS_ERR(bl)) { 1589 if (IS_ERR(bl)) {
1379 dev_err(parent, "unable to register backlight device: %ld\n", 1590 dev_err(parent, "unable to register backlight device: %ld\n",
@@ -1381,7 +1592,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
1381 return NULL; 1592 return NULL;
1382 } 1593 }
1383 1594
1384 bl->props.max_brightness = ch->cfg.bl_info.max_brightness; 1595 bl->props.max_brightness = ch->cfg->bl_info.max_brightness;
1385 bl->props.brightness = bl->props.max_brightness; 1596 bl->props.brightness = bl->props.max_brightness;
1386 backlight_update_status(bl); 1597 backlight_update_status(bl);
1387 1598
@@ -1393,6 +1604,10 @@ static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev)
1393 backlight_device_unregister(bdev); 1604 backlight_device_unregister(bdev);
1394} 1605}
1395 1606
1607/* -----------------------------------------------------------------------------
1608 * Power management
1609 */
1610
1396static int sh_mobile_lcdc_suspend(struct device *dev) 1611static int sh_mobile_lcdc_suspend(struct device *dev)
1397{ 1612{
1398 struct platform_device *pdev = to_platform_device(dev); 1613 struct platform_device *pdev = to_platform_device(dev);
@@ -1436,6 +1651,10 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
1436 .runtime_resume = sh_mobile_lcdc_runtime_resume, 1651 .runtime_resume = sh_mobile_lcdc_runtime_resume,
1437}; 1652};
1438 1653
1654/* -----------------------------------------------------------------------------
1655 * Framebuffer notifier
1656 */
1657
1439/* locking: called with info->lock held */ 1658/* locking: called with info->lock held */
1440static int sh_mobile_lcdc_notify(struct notifier_block *nb, 1659static int sh_mobile_lcdc_notify(struct notifier_block *nb,
1441 unsigned long action, void *data) 1660 unsigned long action, void *data)
@@ -1443,7 +1662,6 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
1443 struct fb_event *event = data; 1662 struct fb_event *event = data;
1444 struct fb_info *info = event->info; 1663 struct fb_info *info = event->info;
1445 struct sh_mobile_lcdc_chan *ch = info->par; 1664 struct sh_mobile_lcdc_chan *ch = info->par;
1446 struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
1447 1665
1448 if (&ch->lcdc->notifier != nb) 1666 if (&ch->lcdc->notifier != nb)
1449 return NOTIFY_DONE; 1667 return NOTIFY_DONE;
@@ -1453,10 +1671,7 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
1453 1671
1454 switch(action) { 1672 switch(action) {
1455 case FB_EVENT_SUSPEND: 1673 case FB_EVENT_SUSPEND:
1456 if (board_cfg->display_off && try_module_get(board_cfg->owner)) { 1674 sh_mobile_lcdc_display_off(ch);
1457 board_cfg->display_off(board_cfg->board_data);
1458 module_put(board_cfg->owner);
1459 }
1460 sh_mobile_lcdc_stop(ch->lcdc); 1675 sh_mobile_lcdc_stop(ch->lcdc);
1461 break; 1676 break;
1462 case FB_EVENT_RESUME: 1677 case FB_EVENT_RESUME:
@@ -1464,47 +1679,60 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
1464 sh_mobile_fb_reconfig(info); 1679 sh_mobile_fb_reconfig(info);
1465 mutex_unlock(&ch->open_lock); 1680 mutex_unlock(&ch->open_lock);
1466 1681
1467 /* HDMI must be enabled before LCDC configuration */ 1682 sh_mobile_lcdc_display_on(ch);
1468 if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
1469 board_cfg->display_on(board_cfg->board_data, info);
1470 module_put(board_cfg->owner);
1471 }
1472
1473 sh_mobile_lcdc_start(ch->lcdc); 1683 sh_mobile_lcdc_start(ch->lcdc);
1474 } 1684 }
1475 1685
1476 return NOTIFY_OK; 1686 return NOTIFY_OK;
1477} 1687}
1478 1688
1689/* -----------------------------------------------------------------------------
1690 * Probe/remove and driver init/exit
1691 */
1692
1693static const struct fb_videomode default_720p __devinitconst = {
1694 .name = "HDMI 720p",
1695 .xres = 1280,
1696 .yres = 720,
1697
1698 .left_margin = 220,
1699 .right_margin = 110,
1700 .hsync_len = 40,
1701
1702 .upper_margin = 20,
1703 .lower_margin = 5,
1704 .vsync_len = 5,
1705
1706 .pixclock = 13468,
1707 .refresh = 60,
1708 .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
1709};
1710
1479static int sh_mobile_lcdc_remove(struct platform_device *pdev) 1711static int sh_mobile_lcdc_remove(struct platform_device *pdev)
1480{ 1712{
1481 struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); 1713 struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
1482 struct fb_info *info;
1483 int i; 1714 int i;
1484 1715
1485 fb_unregister_client(&priv->notifier); 1716 fb_unregister_client(&priv->notifier);
1486 1717
1487 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) 1718 for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
1488 if (priv->ch[i].info && priv->ch[i].info->dev) 1719 sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]);
1489 unregister_framebuffer(priv->ch[i].info);
1490 1720
1491 sh_mobile_lcdc_stop(priv); 1721 sh_mobile_lcdc_stop(priv);
1492 1722
1493 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { 1723 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
1494 info = priv->ch[i].info; 1724 struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
1495 1725
1496 if (!info || !info->device) 1726 if (ch->tx_dev) {
1497 continue; 1727 ch->tx_dev->lcdc = NULL;
1728 module_put(ch->cfg->tx_dev->dev.driver->owner);
1729 }
1498 1730
1499 if (priv->ch[i].sglist) 1731 sh_mobile_lcdc_channel_fb_cleanup(ch);
1500 vfree(priv->ch[i].sglist);
1501 1732
1502 if (info->screen_base) 1733 if (ch->fb_mem)
1503 dma_free_coherent(&pdev->dev, info->fix.smem_len, 1734 dma_free_coherent(&pdev->dev, ch->fb_size,
1504 info->screen_base, 1735 ch->fb_mem, ch->dma_handle);
1505 priv->ch[i].dma_handle);
1506 fb_dealloc_cmap(&info->cmap);
1507 framebuffer_release(info);
1508 } 1736 }
1509 1737
1510 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { 1738 for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
@@ -1512,11 +1740,10 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
1512 sh_mobile_lcdc_bl_remove(priv->ch[i].bl); 1740 sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
1513 } 1741 }
1514 1742
1515 if (priv->dot_clk) 1743 if (priv->dot_clk) {
1744 pm_runtime_disable(&pdev->dev);
1516 clk_put(priv->dot_clk); 1745 clk_put(priv->dot_clk);
1517 1746 }
1518 if (priv->dev)
1519 pm_runtime_disable(priv->dev);
1520 1747
1521 if (priv->base) 1748 if (priv->base)
1522 iounmap(priv->base); 1749 iounmap(priv->base);
@@ -1527,49 +1754,82 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
1527 return 0; 1754 return 0;
1528} 1755}
1529 1756
1530static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch, 1757static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
1531 struct device *dev)
1532{ 1758{
1533 struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg; 1759 int interface_type = ch->cfg->interface_type;
1760
1761 switch (interface_type) {
1762 case RGB8:
1763 case RGB9:
1764 case RGB12A:
1765 case RGB12B:
1766 case RGB16:
1767 case RGB18:
1768 case RGB24:
1769 case SYS8A:
1770 case SYS8B:
1771 case SYS8C:
1772 case SYS8D:
1773 case SYS9:
1774 case SYS12:
1775 case SYS16A:
1776 case SYS16B:
1777 case SYS16C:
1778 case SYS18:
1779 case SYS24:
1780 break;
1781 default:
1782 return -EINVAL;
1783 }
1784
1785 /* SUBLCD only supports SYS interface */
1786 if (lcdc_chan_is_sublcd(ch)) {
1787 if (!(interface_type & LDMT1R_IFM))
1788 return -EINVAL;
1789
1790 interface_type &= ~LDMT1R_IFM;
1791 }
1792
1793 ch->ldmt1r_value = interface_type;
1794 return 0;
1795}
1796
1797static int __devinit
1798sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
1799 struct sh_mobile_lcdc_chan *ch)
1800{
1801 const struct sh_mobile_lcdc_format_info *format;
1802 const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg;
1534 const struct fb_videomode *max_mode; 1803 const struct fb_videomode *max_mode;
1535 const struct fb_videomode *mode; 1804 const struct fb_videomode *mode;
1536 struct fb_var_screeninfo *var; 1805 unsigned int num_modes;
1537 struct fb_info *info;
1538 unsigned int max_size; 1806 unsigned int max_size;
1539 int num_cfg; 1807 unsigned int i;
1540 void *buf;
1541 int ret;
1542 int i;
1543 1808
1544 mutex_init(&ch->open_lock); 1809 mutex_init(&ch->open_lock);
1810 ch->notify = sh_mobile_lcdc_display_notify;
1545 1811
1546 /* Allocate the frame buffer device. */ 1812 /* Validate the format. */
1547 ch->info = framebuffer_alloc(0, dev); 1813 format = sh_mobile_format_info(cfg->fourcc);
1548 if (!ch->info) { 1814 if (format == NULL) {
1549 dev_err(dev, "unable to allocate fb_info\n"); 1815 dev_err(priv->dev, "Invalid FOURCC %08x.\n", cfg->fourcc);
1550 return -ENOMEM; 1816 return -EINVAL;
1551 } 1817 }
1552 1818
1553 info = ch->info;
1554 info->fbops = &sh_mobile_lcdc_ops;
1555 info->par = ch;
1556 info->pseudo_palette = &ch->pseudo_palette;
1557 info->flags = FBINFO_FLAG_DEFAULT;
1558
1559 /* Iterate through the modes to validate them and find the highest 1819 /* Iterate through the modes to validate them and find the highest
1560 * resolution. 1820 * resolution.
1561 */ 1821 */
1562 max_mode = NULL; 1822 max_mode = NULL;
1563 max_size = 0; 1823 max_size = 0;
1564 1824
1565 for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) { 1825 for (i = 0, mode = cfg->lcd_modes; i < cfg->num_modes; i++, mode++) {
1566 unsigned int size = mode->yres * mode->xres; 1826 unsigned int size = mode->yres * mode->xres;
1567 1827
1568 /* NV12/NV21 buffers must have even number of lines */ 1828 /* NV12/NV21 buffers must have even number of lines */
1569 if ((cfg->fourcc == V4L2_PIX_FMT_NV12 || 1829 if ((cfg->fourcc == V4L2_PIX_FMT_NV12 ||
1570 cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) { 1830 cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) {
1571 dev_err(dev, "yres must be multiple of 2 for YCbCr420 " 1831 dev_err(priv->dev, "yres must be multiple of 2 for "
1572 "mode.\n"); 1832 "YCbCr420 mode.\n");
1573 return -EINVAL; 1833 return -EINVAL;
1574 } 1834 }
1575 1835
@@ -1582,93 +1842,59 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
1582 if (!max_size) 1842 if (!max_size)
1583 max_size = MAX_XRES * MAX_YRES; 1843 max_size = MAX_XRES * MAX_YRES;
1584 else 1844 else
1585 dev_dbg(dev, "Found largest videomode %ux%u\n", 1845 dev_dbg(priv->dev, "Found largest videomode %ux%u\n",
1586 max_mode->xres, max_mode->yres); 1846 max_mode->xres, max_mode->yres);
1587 1847
1588 /* Create the mode list. */ 1848 if (cfg->lcd_modes == NULL) {
1589 if (cfg->lcd_cfg == NULL) {
1590 mode = &default_720p; 1849 mode = &default_720p;
1591 num_cfg = 1; 1850 num_modes = 1;
1592 } else { 1851 } else {
1593 mode = cfg->lcd_cfg; 1852 mode = cfg->lcd_modes;
1594 num_cfg = cfg->num_cfg; 1853 num_modes = cfg->num_modes;
1595 } 1854 }
1596 1855
1597 fb_videomode_to_modelist(mode, num_cfg, &info->modelist); 1856 /* Use the first mode as default. */
1857 ch->format = format;
1858 ch->xres = mode->xres;
1859 ch->xres_virtual = mode->xres;
1860 ch->yres = mode->yres;
1861 ch->yres_virtual = mode->yres * 2;
1598 1862
1599 /* Initialize variable screen information using the first mode as 1863 if (!format->yuv) {
1600 * default. The default Y virtual resolution is twice the panel size to 1864 ch->colorspace = V4L2_COLORSPACE_SRGB;
1601 * allow for double-buffering. 1865 ch->pitch = ch->xres * format->bpp / 8;
1602 */ 1866 } else {
1603 var = &info->var; 1867 ch->colorspace = V4L2_COLORSPACE_REC709;
1604 fb_videomode_to_var(var, mode); 1868 ch->pitch = ch->xres;
1605 var->width = cfg->lcd_size_cfg.width;
1606 var->height = cfg->lcd_size_cfg.height;
1607 var->yres_virtual = var->yres * 2;
1608 var->activate = FB_ACTIVATE_NOW;
1609
1610 switch (cfg->fourcc) {
1611 case V4L2_PIX_FMT_RGB565:
1612 var->bits_per_pixel = 16;
1613 break;
1614 case V4L2_PIX_FMT_BGR24:
1615 var->bits_per_pixel = 24;
1616 break;
1617 case V4L2_PIX_FMT_BGR32:
1618 var->bits_per_pixel = 32;
1619 break;
1620 default:
1621 var->grayscale = cfg->fourcc;
1622 break;
1623 } 1869 }
1624 1870
1625 /* Make sure the memory size check won't fail. smem_len is initialized 1871 ch->display.width = cfg->panel_cfg.width;
1626 * later based on var. 1872 ch->display.height = cfg->panel_cfg.height;
1627 */ 1873 ch->display.mode = *mode;
1628 info->fix.smem_len = UINT_MAX;
1629 ret = sh_mobile_check_var(var, info);
1630 if (ret)
1631 return ret;
1632
1633 max_size = max_size * var->bits_per_pixel / 8 * 2;
1634 1874
1635 /* Allocate frame buffer memory and color map. */ 1875 /* Allocate frame buffer memory. */
1636 buf = dma_alloc_coherent(dev, max_size, &ch->dma_handle, GFP_KERNEL); 1876 ch->fb_size = max_size * format->bpp / 8 * 2;
1637 if (!buf) { 1877 ch->fb_mem = dma_alloc_coherent(priv->dev, ch->fb_size, &ch->dma_handle,
1638 dev_err(dev, "unable to allocate buffer\n"); 1878 GFP_KERNEL);
1879 if (ch->fb_mem == NULL) {
1880 dev_err(priv->dev, "unable to allocate buffer\n");
1639 return -ENOMEM; 1881 return -ENOMEM;
1640 } 1882 }
1641 1883
1642 ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); 1884 /* Initialize the transmitter device if present. */
1643 if (ret < 0) { 1885 if (cfg->tx_dev) {
1644 dev_err(dev, "unable to allocate cmap\n"); 1886 if (!cfg->tx_dev->dev.driver ||
1645 dma_free_coherent(dev, max_size, buf, ch->dma_handle); 1887 !try_module_get(cfg->tx_dev->dev.driver->owner)) {
1646 return ret; 1888 dev_warn(priv->dev,
1647 } 1889 "unable to get transmitter device\n");
1648 1890 return -EINVAL;
1649 /* Initialize fixed screen information. Restrict pan to 2 lines steps 1891 }
1650 * for NV12 and NV21. 1892 ch->tx_dev = platform_get_drvdata(cfg->tx_dev);
1651 */ 1893 ch->tx_dev->lcdc = ch;
1652 info->fix = sh_mobile_lcdc_fix; 1894 ch->tx_dev->def_mode = *mode;
1653 info->fix.smem_start = ch->dma_handle;
1654 info->fix.smem_len = max_size;
1655 if (cfg->fourcc == V4L2_PIX_FMT_NV12 ||
1656 cfg->fourcc == V4L2_PIX_FMT_NV21)
1657 info->fix.ypanstep = 2;
1658
1659 if (sh_mobile_format_is_yuv(var)) {
1660 info->fix.line_length = var->xres;
1661 info->fix.visual = FB_VISUAL_FOURCC;
1662 } else {
1663 info->fix.line_length = var->xres * var->bits_per_pixel / 8;
1664 info->fix.visual = FB_VISUAL_TRUECOLOR;
1665 } 1895 }
1666 1896
1667 info->screen_base = buf; 1897 return sh_mobile_lcdc_channel_fb_init(ch, mode, num_modes);
1668 info->device = dev;
1669 ch->display_var = *var;
1670
1671 return 0;
1672} 1898}
1673 1899
1674static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) 1900static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
@@ -1698,6 +1924,8 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1698 return -ENOMEM; 1924 return -ENOMEM;
1699 } 1925 }
1700 1926
1927 priv->dev = &pdev->dev;
1928 priv->meram_dev = pdata->meram_dev;
1701 platform_set_drvdata(pdev, priv); 1929 platform_set_drvdata(pdev, priv);
1702 1930
1703 error = request_irq(i, sh_mobile_lcdc_irq, 0, 1931 error = request_irq(i, sh_mobile_lcdc_irq, 0,
@@ -1714,7 +1942,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1714 struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels; 1942 struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels;
1715 1943
1716 ch->lcdc = priv; 1944 ch->lcdc = priv;
1717 memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i])); 1945 ch->cfg = &pdata->ch[i];
1718 1946
1719 error = sh_mobile_lcdc_check_interface(ch); 1947 error = sh_mobile_lcdc_check_interface(ch);
1720 if (error) { 1948 if (error) {
@@ -1726,7 +1954,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1726 ch->pan_offset = 0; 1954 ch->pan_offset = 0;
1727 1955
1728 /* probe the backlight is there is one defined */ 1956 /* probe the backlight is there is one defined */
1729 if (ch->cfg.bl_info.max_brightness) 1957 if (ch->cfg->bl_info.max_brightness)
1730 ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch); 1958 ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch);
1731 1959
1732 switch (pdata->ch[i].chan) { 1960 switch (pdata->ch[i].chan) {
@@ -1757,18 +1985,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1757 if (!priv->base) 1985 if (!priv->base)
1758 goto err1; 1986 goto err1;
1759 1987
1760 error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); 1988 error = sh_mobile_lcdc_setup_clocks(priv, pdata->clock_source);
1761 if (error) { 1989 if (error) {
1762 dev_err(&pdev->dev, "unable to setup clocks\n"); 1990 dev_err(&pdev->dev, "unable to setup clocks\n");
1763 goto err1; 1991 goto err1;
1764 } 1992 }
1765 1993
1766 priv->meram_dev = pdata->meram_dev; 1994 /* Enable runtime PM. */
1995 pm_runtime_enable(&pdev->dev);
1767 1996
1768 for (i = 0; i < num_channels; i++) { 1997 for (i = 0; i < num_channels; i++) {
1769 struct sh_mobile_lcdc_chan *ch = priv->ch + i; 1998 struct sh_mobile_lcdc_chan *ch = priv->ch + i;
1770 1999
1771 error = sh_mobile_lcdc_channel_init(ch, &pdev->dev); 2000 error = sh_mobile_lcdc_channel_init(priv, ch);
1772 if (error) 2001 if (error)
1773 goto err1; 2002 goto err1;
1774 } 2003 }
@@ -1781,31 +2010,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1781 2010
1782 for (i = 0; i < num_channels; i++) { 2011 for (i = 0; i < num_channels; i++) {
1783 struct sh_mobile_lcdc_chan *ch = priv->ch + i; 2012 struct sh_mobile_lcdc_chan *ch = priv->ch + i;
1784 struct fb_info *info = ch->info;
1785
1786 if (info->fbdefio) {
1787 ch->sglist = vmalloc(sizeof(struct scatterlist) *
1788 info->fix.smem_len >> PAGE_SHIFT);
1789 if (!ch->sglist) {
1790 dev_err(&pdev->dev, "cannot allocate sglist\n");
1791 goto err1;
1792 }
1793 }
1794
1795 info->bl_dev = ch->bl;
1796 2013
1797 error = register_framebuffer(info); 2014 error = sh_mobile_lcdc_channel_fb_register(ch);
1798 if (error < 0) 2015 if (error)
1799 goto err1; 2016 goto err1;
1800
1801 dev_info(info->dev, "registered %s/%s as %dx%d %dbpp.\n",
1802 pdev->name, (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
1803 "mainlcd" : "sublcd", info->var.xres, info->var.yres,
1804 info->var.bits_per_pixel);
1805
1806 /* deferred io mode: disable clock to save power */
1807 if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
1808 sh_mobile_lcdc_clk_off(priv);
1809 } 2017 }
1810 2018
1811 /* Failure ignored */ 2019 /* Failure ignored */
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index a58a0f38848b..da1c26e78a57 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -14,9 +14,35 @@ enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
14 14
15#define PALETTE_NR 16 15#define PALETTE_NR 16
16 16
17struct sh_mobile_lcdc_priv;
18struct fb_info;
19struct backlight_device; 17struct backlight_device;
18struct fb_info;
19struct module;
20struct sh_mobile_lcdc_chan;
21struct sh_mobile_lcdc_entity;
22struct sh_mobile_lcdc_format_info;
23struct sh_mobile_lcdc_priv;
24
25#define SH_MOBILE_LCDC_DISPLAY_DISCONNECTED 0
26#define SH_MOBILE_LCDC_DISPLAY_CONNECTED 1
27
28struct sh_mobile_lcdc_entity_ops {
29 /* Display */
30 int (*display_on)(struct sh_mobile_lcdc_entity *entity);
31 void (*display_off)(struct sh_mobile_lcdc_entity *entity);
32};
33
34enum sh_mobile_lcdc_entity_event {
35 SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT,
36 SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT,
37 SH_MOBILE_LCDC_EVENT_DISPLAY_MODE,
38};
39
40struct sh_mobile_lcdc_entity {
41 struct module *owner;
42 const struct sh_mobile_lcdc_entity_ops *ops;
43 struct sh_mobile_lcdc_chan *lcdc;
44 struct fb_videomode def_mode;
45};
20 46
21/* 47/*
22 * struct sh_mobile_lcdc_chan - LCDC display channel 48 * struct sh_mobile_lcdc_chan - LCDC display channel
@@ -27,29 +53,57 @@ struct backlight_device;
27 */ 53 */
28struct sh_mobile_lcdc_chan { 54struct sh_mobile_lcdc_chan {
29 struct sh_mobile_lcdc_priv *lcdc; 55 struct sh_mobile_lcdc_priv *lcdc;
56 struct sh_mobile_lcdc_entity *tx_dev;
57 const struct sh_mobile_lcdc_chan_cfg *cfg;
58
30 unsigned long *reg_offs; 59 unsigned long *reg_offs;
31 unsigned long ldmt1r_value; 60 unsigned long ldmt1r_value;
32 unsigned long enabled; /* ME and SE in LDCNT2R */ 61 unsigned long enabled; /* ME and SE in LDCNT2R */
33 struct sh_mobile_lcdc_chan_cfg cfg; 62 void *meram;
34 u32 pseudo_palette[PALETTE_NR]; 63
35 struct fb_info *info; 64 struct mutex open_lock; /* protects the use counter */
36 struct backlight_device *bl; 65 int use_count;
66
67 void *fb_mem;
68 unsigned long fb_size;
69
37 dma_addr_t dma_handle; 70 dma_addr_t dma_handle;
38 struct fb_deferred_io defio;
39 struct scatterlist *sglist;
40 unsigned long frame_end;
41 unsigned long pan_offset; 71 unsigned long pan_offset;
72
73 unsigned long frame_end;
42 wait_queue_head_t frame_end_wait; 74 wait_queue_head_t frame_end_wait;
43 struct completion vsync_completion; 75 struct completion vsync_completion;
44 struct fb_var_screeninfo display_var; 76
45 int use_count; 77 const struct sh_mobile_lcdc_format_info *format;
46 int blank_status; 78 u32 colorspace;
47 struct mutex open_lock; /* protects the use counter */ 79 unsigned int xres;
48 int meram_enabled; 80 unsigned int xres_virtual;
81 unsigned int yres;
82 unsigned int yres_virtual;
83 unsigned int pitch;
49 84
50 unsigned long base_addr_y; 85 unsigned long base_addr_y;
51 unsigned long base_addr_c; 86 unsigned long base_addr_c;
52 unsigned int pitch; 87
88 int (*notify)(struct sh_mobile_lcdc_chan *ch,
89 enum sh_mobile_lcdc_entity_event event,
90 const struct fb_videomode *mode,
91 const struct fb_monspecs *monspec);
92
93 /* Backlight */
94 struct backlight_device *bl;
95
96 /* FB */
97 struct fb_info *info;
98 u32 pseudo_palette[PALETTE_NR];
99 struct {
100 unsigned int width;
101 unsigned int height;
102 struct fb_videomode mode;
103 } display;
104 struct fb_deferred_io defio;
105 struct scatterlist *sglist;
106 int blank_status;
53}; 107};
54 108
55#endif 109#endif
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index f45d83ecfd21..82ba830bf95d 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -9,16 +9,22 @@
9 * for more details. 9 * for more details.
10 */ 10 */
11 11
12#include <linux/device.h>
13#include <linux/err.h>
14#include <linux/genalloc.h>
15#include <linux/io.h>
12#include <linux/kernel.h> 16#include <linux/kernel.h>
13#include <linux/module.h> 17#include <linux/module.h>
14#include <linux/device.h> 18#include <linux/platform_device.h>
15#include <linux/pm_runtime.h> 19#include <linux/pm_runtime.h>
16#include <linux/io.h>
17#include <linux/slab.h> 20#include <linux/slab.h>
18#include <linux/platform_device.h> 21
19#include <video/sh_mobile_meram.h> 22#include <video/sh_mobile_meram.h>
20 23
21/* meram registers */ 24/* -----------------------------------------------------------------------------
25 * MERAM registers
26 */
27
22#define MEVCR1 0x4 28#define MEVCR1 0x4
23#define MEVCR1_RST (1 << 31) 29#define MEVCR1_RST (1 << 31)
24#define MEVCR1_WD (1 << 30) 30#define MEVCR1_WD (1 << 30)
@@ -81,16 +87,14 @@
81 ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \ 87 ((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
82 ((xszm1) << MExxBSIZE_XSZM1_SHIFT)) 88 ((xszm1) << MExxBSIZE_XSZM1_SHIFT))
83 89
84#define SH_MOBILE_MERAM_ICB_NUM 32 90static const unsigned long common_regs[] = {
85
86static unsigned long common_regs[] = {
87 MEVCR1, 91 MEVCR1,
88 MEQSEL1, 92 MEQSEL1,
89 MEQSEL2, 93 MEQSEL2,
90}; 94};
91#define CMN_REGS_SIZE ARRAY_SIZE(common_regs) 95#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
92 96
93static unsigned long icb_regs[] = { 97static const unsigned long icb_regs[] = {
94 MExxCTL, 98 MExxCTL,
95 MExxBSIZE, 99 MExxBSIZE,
96 MExxMNCF, 100 MExxMNCF,
@@ -100,216 +104,269 @@ static unsigned long icb_regs[] = {
100}; 104};
101#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs) 105#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
102 106
107/*
108 * sh_mobile_meram_icb - MERAM ICB information
109 * @regs: Registers cache
110 * @index: ICB index
111 * @offset: MERAM block offset
112 * @size: MERAM block size in KiB
113 * @cache_unit: Bytes to cache per ICB
114 * @pixelformat: Video pixel format of the data stored in the ICB
115 * @current_reg: Which of Start Address Register A (0) or B (1) is in use
116 */
117struct sh_mobile_meram_icb {
118 unsigned long regs[ICB_REGS_SIZE];
119 unsigned int index;
120 unsigned long offset;
121 unsigned int size;
122
123 unsigned int cache_unit;
124 unsigned int pixelformat;
125 unsigned int current_reg;
126};
127
128#define MERAM_ICB_NUM 32
129
130struct sh_mobile_meram_fb_plane {
131 struct sh_mobile_meram_icb *marker;
132 struct sh_mobile_meram_icb *cache;
133};
134
135struct sh_mobile_meram_fb_cache {
136 unsigned int nplanes;
137 struct sh_mobile_meram_fb_plane planes[2];
138};
139
140/*
141 * sh_mobile_meram_priv - MERAM device
142 * @base: Registers base address
143 * @meram: MERAM physical address
144 * @regs: Registers cache
145 * @lock: Protects used_icb and icbs
146 * @used_icb: Bitmask of used ICBs
147 * @icbs: ICBs
148 * @pool: Allocation pool to manage the MERAM
149 */
103struct sh_mobile_meram_priv { 150struct sh_mobile_meram_priv {
104 void __iomem *base; 151 void __iomem *base;
105 struct mutex lock; 152 unsigned long meram;
106 unsigned long used_icb; 153 unsigned long regs[MERAM_REGS_SIZE];
107 int used_meram_cache_regions; 154
108 unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM]; 155 struct mutex lock;
109 unsigned long cmn_saved_regs[CMN_REGS_SIZE]; 156 unsigned long used_icb;
110 unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM]; 157 struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
158
159 struct gen_pool *pool;
111}; 160};
112 161
113/* settings */ 162/* settings */
114#define MERAM_SEC_LINE 15 163#define MERAM_GRANULARITY 1024
115#define MERAM_LINE_WIDTH 2048 164#define MERAM_SEC_LINE 15
165#define MERAM_LINE_WIDTH 2048
116 166
117/* 167/* -----------------------------------------------------------------------------
118 * MERAM/ICB access functions 168 * Registers access
119 */ 169 */
120 170
121#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20) 171#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
122 172
123static inline void meram_write_icb(void __iomem *base, int idx, int off, 173static inline void meram_write_icb(void __iomem *base, unsigned int idx,
124 unsigned long val) 174 unsigned int off, unsigned long val)
125{ 175{
126 iowrite32(val, MERAM_ICB_OFFSET(base, idx, off)); 176 iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
127} 177}
128 178
129static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off) 179static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
180 unsigned int off)
130{ 181{
131 return ioread32(MERAM_ICB_OFFSET(base, idx, off)); 182 return ioread32(MERAM_ICB_OFFSET(base, idx, off));
132} 183}
133 184
134static inline void meram_write_reg(void __iomem *base, int off, 185static inline void meram_write_reg(void __iomem *base, unsigned int off,
135 unsigned long val) 186 unsigned long val)
136{ 187{
137 iowrite32(val, base + off); 188 iowrite32(val, base + off);
138} 189}
139 190
140static inline unsigned long meram_read_reg(void __iomem *base, int off) 191static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
141{ 192{
142 return ioread32(base + off); 193 return ioread32(base + off);
143} 194}
144 195
145/* 196/* -----------------------------------------------------------------------------
146 * register ICB 197 * Allocation
147 */
148
149#define MERAM_CACHE_START(p) ((p) >> 16)
150#define MERAM_CACHE_END(p) ((p) & 0xffff)
151#define MERAM_CACHE_SET(o, s) ((((o) & 0xffff) << 16) | \
152 (((o) + (s) - 1) & 0xffff))
153
154/*
155 * check if there's no overlaps in MERAM allocation.
156 */ 198 */
157 199
158static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv, 200/* Allocate ICBs and MERAM for a plane. */
159 struct sh_mobile_meram_icb *new) 201static int __meram_alloc(struct sh_mobile_meram_priv *priv,
202 struct sh_mobile_meram_fb_plane *plane,
203 size_t size)
160{ 204{
161 int i; 205 unsigned long mem;
162 int used_start, used_end, meram_start, meram_end; 206 unsigned long idx;
163 207
164 /* valid ICB? */ 208 idx = find_first_zero_bit(&priv->used_icb, 28);
165 if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f) 209 if (idx == 28)
166 return 1; 210 return -ENOMEM;
211 plane->cache = &priv->icbs[idx];
167 212
168 if (test_bit(new->marker_icb, &priv->used_icb) || 213 idx = find_next_zero_bit(&priv->used_icb, 32, 28);
169 test_bit(new->cache_icb, &priv->used_icb)) 214 if (idx == 32)
170 return 1; 215 return -ENOMEM;
216 plane->marker = &priv->icbs[idx];
171 217
172 for (i = 0; i < priv->used_meram_cache_regions; i++) { 218 mem = gen_pool_alloc(priv->pool, size * 1024);
173 used_start = MERAM_CACHE_START(priv->used_meram_cache[i]); 219 if (mem == 0)
174 used_end = MERAM_CACHE_END(priv->used_meram_cache[i]); 220 return -ENOMEM;
175 meram_start = new->meram_offset;
176 meram_end = new->meram_offset + new->meram_size;
177 221
178 if ((meram_start >= used_start && meram_start < used_end) || 222 __set_bit(plane->marker->index, &priv->used_icb);
179 (meram_end > used_start && meram_end < used_end)) 223 __set_bit(plane->cache->index, &priv->used_icb);
180 return 1; 224
181 } 225 plane->marker->offset = mem - priv->meram;
226 plane->marker->size = size;
182 227
183 return 0; 228 return 0;
184} 229}
185 230
186/* 231/* Free ICBs and MERAM for a plane. */
187 * mark the specified ICB as used 232static void __meram_free(struct sh_mobile_meram_priv *priv,
188 */ 233 struct sh_mobile_meram_fb_plane *plane)
234{
235 gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
236 plane->marker->size * 1024);
189 237
190static inline void meram_mark(struct sh_mobile_meram_priv *priv, 238 __clear_bit(plane->marker->index, &priv->used_icb);
191 struct sh_mobile_meram_icb *new) 239 __clear_bit(plane->cache->index, &priv->used_icb);
240}
241
242/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
243static int is_nvcolor(int cspace)
192{ 244{
193 int n; 245 if (cspace == SH_MOBILE_MERAM_PF_NV ||
246 cspace == SH_MOBILE_MERAM_PF_NV24)
247 return 1;
248 return 0;
249}
194 250
195 if (new->marker_icb < 0 || new->cache_icb < 0) 251/* Allocate memory for the ICBs and mark them as used. */
196 return; 252static struct sh_mobile_meram_fb_cache *
253meram_alloc(struct sh_mobile_meram_priv *priv,
254 const struct sh_mobile_meram_cfg *cfg,
255 int pixelformat)
256{
257 struct sh_mobile_meram_fb_cache *cache;
258 unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
259 int ret;
197 260
198 __set_bit(new->marker_icb, &priv->used_icb); 261 if (cfg->icb[0].meram_size == 0)
199 __set_bit(new->cache_icb, &priv->used_icb); 262 return ERR_PTR(-EINVAL);
200 263
201 n = priv->used_meram_cache_regions; 264 if (nplanes == 2 && cfg->icb[1].meram_size == 0)
265 return ERR_PTR(-EINVAL);
202 266
203 priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset, 267 cache = kzalloc(sizeof(*cache), GFP_KERNEL);
204 new->meram_size); 268 if (cache == NULL)
269 return ERR_PTR(-ENOMEM);
205 270
206 priv->used_meram_cache_regions++; 271 cache->nplanes = nplanes;
207}
208 272
209/* 273 ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
210 * unmark the specified ICB as used 274 if (ret < 0)
211 */ 275 goto error;
212 276
213static inline void meram_unmark(struct sh_mobile_meram_priv *priv, 277 cache->planes[0].marker->current_reg = 1;
214 struct sh_mobile_meram_icb *icb) 278 cache->planes[0].marker->pixelformat = pixelformat;
215{ 279
216 int i; 280 if (cache->nplanes == 1)
217 unsigned long pattern; 281 return cache;
218 282
219 if (icb->marker_icb < 0 || icb->cache_icb < 0) 283 ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
220 return; 284 if (ret < 0) {
221 285 __meram_free(priv, &cache->planes[0]);
222 __clear_bit(icb->marker_icb, &priv->used_icb); 286 goto error;
223 __clear_bit(icb->cache_icb, &priv->used_icb);
224
225 pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size);
226 for (i = 0; i < priv->used_meram_cache_regions; i++) {
227 if (priv->used_meram_cache[i] == pattern) {
228 while (i < priv->used_meram_cache_regions - 1) {
229 priv->used_meram_cache[i] =
230 priv->used_meram_cache[i + 1] ;
231 i++;
232 }
233 priv->used_meram_cache[i] = 0;
234 priv->used_meram_cache_regions--;
235 break;
236 }
237 } 287 }
288
289 return cache;
290
291error:
292 kfree(cache);
293 return ERR_PTR(-ENOMEM);
238} 294}
239 295
240/* 296/* Unmark the specified ICB as used. */
241 * is this a YCbCr(NV12, NV16 or NV24) colorspace 297static void meram_free(struct sh_mobile_meram_priv *priv,
242 */ 298 struct sh_mobile_meram_fb_cache *cache)
243static inline int is_nvcolor(int cspace)
244{ 299{
245 if (cspace == SH_MOBILE_MERAM_PF_NV || 300 __meram_free(priv, &cache->planes[0]);
246 cspace == SH_MOBILE_MERAM_PF_NV24) 301 if (cache->nplanes == 2)
247 return 1; 302 __meram_free(priv, &cache->planes[1]);
248 return 0; 303
304 kfree(cache);
249} 305}
250 306
251/* 307/* Set the next address to fetch. */
252 * set the next address to fetch 308static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
253 */ 309 struct sh_mobile_meram_fb_cache *cache,
254static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv, 310 unsigned long base_addr_y,
255 struct sh_mobile_meram_cfg *cfg, 311 unsigned long base_addr_c)
256 unsigned long base_addr_y,
257 unsigned long base_addr_c)
258{ 312{
313 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
259 unsigned long target; 314 unsigned long target;
260 315
261 target = (cfg->current_reg) ? MExxSARA : MExxSARB; 316 icb->current_reg ^= 1;
262 cfg->current_reg ^= 1; 317 target = icb->current_reg ? MExxSARB : MExxSARA;
263 318
264 /* set the next address to fetch */ 319 /* set the next address to fetch */
265 meram_write_icb(priv->base, cfg->icb[0].cache_icb, target, 320 meram_write_icb(priv->base, cache->planes[0].cache->index, target,
266 base_addr_y); 321 base_addr_y);
267 meram_write_icb(priv->base, cfg->icb[0].marker_icb, target, 322 meram_write_icb(priv->base, cache->planes[0].marker->index, target,
268 base_addr_y + cfg->icb[0].cache_unit); 323 base_addr_y + cache->planes[0].marker->cache_unit);
269 324
270 if (is_nvcolor(cfg->pixelformat)) { 325 if (cache->nplanes == 2) {
271 meram_write_icb(priv->base, cfg->icb[1].cache_icb, target, 326 meram_write_icb(priv->base, cache->planes[1].cache->index,
272 base_addr_c); 327 target, base_addr_c);
273 meram_write_icb(priv->base, cfg->icb[1].marker_icb, target, 328 meram_write_icb(priv->base, cache->planes[1].marker->index,
274 base_addr_c + cfg->icb[1].cache_unit); 329 target, base_addr_c +
330 cache->planes[1].marker->cache_unit);
275 } 331 }
276} 332}
277 333
278/* 334/* Get the next ICB address. */
279 * get the next ICB address 335static void
280 */ 336meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
281static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata, 337 struct sh_mobile_meram_fb_cache *cache,
282 struct sh_mobile_meram_cfg *cfg, 338 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
283 unsigned long *icb_addr_y,
284 unsigned long *icb_addr_c)
285{ 339{
340 struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
286 unsigned long icb_offset; 341 unsigned long icb_offset;
287 342
288 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0) 343 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
289 icb_offset = 0x80000000 | (cfg->current_reg << 29); 344 icb_offset = 0x80000000 | (icb->current_reg << 29);
290 else 345 else
291 icb_offset = 0xc0000000 | (cfg->current_reg << 23); 346 icb_offset = 0xc0000000 | (icb->current_reg << 23);
292 347
293 *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24); 348 *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
294 if (is_nvcolor(cfg->pixelformat)) 349 if (cache->nplanes == 2)
295 *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24); 350 *icb_addr_c = icb_offset
351 | (cache->planes[1].marker->index << 24);
296} 352}
297 353
298#define MERAM_CALC_BYTECOUNT(x, y) \ 354#define MERAM_CALC_BYTECOUNT(x, y) \
299 (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1)) 355 (((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
300 356
301/* 357/* Initialize MERAM. */
302 * initialize MERAM
303 */
304
305static int meram_init(struct sh_mobile_meram_priv *priv, 358static int meram_init(struct sh_mobile_meram_priv *priv,
306 struct sh_mobile_meram_icb *icb, 359 struct sh_mobile_meram_fb_plane *plane,
307 int xres, int yres, int *out_pitch) 360 unsigned int xres, unsigned int yres,
361 unsigned int *out_pitch)
308{ 362{
363 struct sh_mobile_meram_icb *marker = plane->marker;
309 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres); 364 unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
310 unsigned long bnm; 365 unsigned long bnm;
311 int lcdc_pitch, xpitch, line_cnt; 366 unsigned int lcdc_pitch;
312 int save_lines; 367 unsigned int xpitch;
368 unsigned int line_cnt;
369 unsigned int save_lines;
313 370
314 /* adjust pitch to 1024, 2048, 4096 or 8192 */ 371 /* adjust pitch to 1024, 2048, 4096 or 8192 */
315 lcdc_pitch = (xres - 1) | 1023; 372 lcdc_pitch = (xres - 1) | 1023;
@@ -322,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
322 lcdc_pitch = xpitch = MERAM_LINE_WIDTH; 379 lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
323 line_cnt = total_byte_count >> 11; 380 line_cnt = total_byte_count >> 11;
324 *out_pitch = xres; 381 *out_pitch = xres;
325 save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE); 382 save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
326 save_lines *= MERAM_SEC_LINE; 383 save_lines *= MERAM_SEC_LINE;
327 } else { 384 } else {
328 xpitch = xres; 385 xpitch = xres;
329 line_cnt = yres; 386 line_cnt = yres;
330 *out_pitch = lcdc_pitch; 387 *out_pitch = lcdc_pitch;
331 save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2; 388 save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
332 save_lines &= 0xff; 389 save_lines &= 0xff;
333 } 390 }
334 bnm = (save_lines - 1) << 16; 391 bnm = (save_lines - 1) << 16;
@@ -336,19 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
336 /* TODO: we better to check if we have enough MERAM buffer size */ 393 /* TODO: we better to check if we have enough MERAM buffer size */
337 394
338 /* set up ICB */ 395 /* set up ICB */
339 meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE, 396 meram_write_icb(priv->base, plane->cache->index, MExxBSIZE,
340 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1)); 397 MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
341 meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE, 398 meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
342 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1)); 399 MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
343 400
344 meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm); 401 meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm);
345 meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm); 402 meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
346 403
347 meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch); 404 meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch);
348 meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch); 405 meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
349 406
350 /* save a cache unit size */ 407 /* save a cache unit size */
351 icb->cache_unit = xres * save_lines; 408 plane->cache->cache_unit = xres * save_lines;
409 plane->marker->cache_unit = xres * save_lines;
352 410
353 /* 411 /*
354 * Set MERAM for framebuffer 412 * Set MERAM for framebuffer
@@ -356,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
356 * we also chain the cache_icb and the marker_icb. 414 * we also chain the cache_icb and the marker_icb.
357 * we also split the allocated MERAM buffer between two ICBs. 415 * we also split the allocated MERAM buffer between two ICBs.
358 */ 416 */
359 meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 417 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
360 MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) | 418 MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
361 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | 419 | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
362 MExxCTL_MD_FB); 420 MExxCTL_MD_FB);
363 meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 421 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
364 MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset + 422 MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
365 icb->meram_size / 2) | 423 plane->marker->size / 2) |
366 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM | 424 MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
367 MExxCTL_MD_FB); 425 MExxCTL_MD_FB);
368 426
@@ -370,239 +428,175 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
370} 428}
371 429
372static void meram_deinit(struct sh_mobile_meram_priv *priv, 430static void meram_deinit(struct sh_mobile_meram_priv *priv,
373 struct sh_mobile_meram_icb *icb) 431 struct sh_mobile_meram_fb_plane *plane)
374{ 432{
375 /* disable ICB */ 433 /* disable ICB */
376 meram_write_icb(priv->base, icb->cache_icb, MExxCTL, 434 meram_write_icb(priv->base, plane->cache->index, MExxCTL,
377 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); 435 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
378 meram_write_icb(priv->base, icb->marker_icb, MExxCTL, 436 meram_write_icb(priv->base, plane->marker->index, MExxCTL,
379 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF); 437 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
380 icb->cache_unit = 0; 438
439 plane->cache->cache_unit = 0;
440 plane->marker->cache_unit = 0;
381} 441}
382 442
383/* 443/* -----------------------------------------------------------------------------
384 * register the ICB 444 * Registration/unregistration
385 */ 445 */
386 446
387static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata, 447static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
388 struct sh_mobile_meram_cfg *cfg, 448 const struct sh_mobile_meram_cfg *cfg,
389 int xres, int yres, int pixelformat, 449 unsigned int xres, unsigned int yres,
390 unsigned long base_addr_y, 450 unsigned int pixelformat,
391 unsigned long base_addr_c, 451 unsigned int *pitch)
392 unsigned long *icb_addr_y,
393 unsigned long *icb_addr_c,
394 int *pitch)
395{ 452{
396 struct platform_device *pdev; 453 struct sh_mobile_meram_fb_cache *cache;
397 struct sh_mobile_meram_priv *priv; 454 struct sh_mobile_meram_priv *priv = pdata->priv;
398 int n, out_pitch; 455 struct platform_device *pdev = pdata->pdev;
399 int error = 0; 456 unsigned int out_pitch;
400
401 if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
402 return -EINVAL;
403 457
404 if (pixelformat != SH_MOBILE_MERAM_PF_NV && 458 if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
405 pixelformat != SH_MOBILE_MERAM_PF_NV24 && 459 pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
406 pixelformat != SH_MOBILE_MERAM_PF_RGB) 460 pixelformat != SH_MOBILE_MERAM_PF_RGB)
407 return -EINVAL; 461 return ERR_PTR(-EINVAL);
408
409 priv = pdata->priv;
410 pdev = pdata->pdev;
411 462
412 dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)", 463 dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
413 xres, yres, (!pixelformat) ? "yuv" : "rgb", 464 !pixelformat ? "yuv" : "rgb");
414 base_addr_y, base_addr_c);
415 465
416 /* we can't handle wider than 8192px */ 466 /* we can't handle wider than 8192px */
417 if (xres > 8192) { 467 if (xres > 8192) {
418 dev_err(&pdev->dev, "width exceeding the limit (> 8192)."); 468 dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
419 return -EINVAL; 469 return ERR_PTR(-EINVAL);
420 }
421
422 /* do we have at least one ICB config? */
423 if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
424 dev_err(&pdev->dev, "at least one ICB is required.");
425 return -EINVAL;
426 } 470 }
427 471
428 mutex_lock(&priv->lock); 472 mutex_lock(&priv->lock);
429 473
430 if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) { 474 /* We now register the ICBs and allocate the MERAM regions. */
431 dev_err(&pdev->dev, "no more ICB available."); 475 cache = meram_alloc(priv, cfg, pixelformat);
432 error = -EINVAL; 476 if (IS_ERR(cache)) {
433 goto err; 477 dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
434 } 478 PTR_ERR(cache));
435
436 /* make sure that there's no overlaps */
437 if (meram_check_overlap(priv, &cfg->icb[0])) {
438 dev_err(&pdev->dev, "conflicting config detected.");
439 error = -EINVAL;
440 goto err; 479 goto err;
441 } 480 }
442 n = 1;
443
444 /* do the same if we have the second ICB set */
445 if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
446 if (meram_check_overlap(priv, &cfg->icb[1])) {
447 dev_err(&pdev->dev, "conflicting config detected.");
448 error = -EINVAL;
449 goto err;
450 }
451 n = 2;
452 }
453
454 if (is_nvcolor(pixelformat) && n != 2) {
455 dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
456 error = -EINVAL;
457 goto err;
458 }
459
460 /* we now register the ICB */
461 cfg->pixelformat = pixelformat;
462 meram_mark(priv, &cfg->icb[0]);
463 if (is_nvcolor(pixelformat))
464 meram_mark(priv, &cfg->icb[1]);
465 481
466 /* initialize MERAM */ 482 /* initialize MERAM */
467 meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch); 483 meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
468 *pitch = out_pitch; 484 *pitch = out_pitch;
469 if (pixelformat == SH_MOBILE_MERAM_PF_NV) 485 if (pixelformat == SH_MOBILE_MERAM_PF_NV)
470 meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2, 486 meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
471 &out_pitch); 487 &out_pitch);
472 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24) 488 else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
473 meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2, 489 meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
474 &out_pitch); 490 &out_pitch);
475 491
476 cfg->current_reg = 1;
477 meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
478 meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
479
480 dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
481 *icb_addr_y, *icb_addr_c);
482
483err: 492err:
484 mutex_unlock(&priv->lock); 493 mutex_unlock(&priv->lock);
485 return error; 494 return cache;
486} 495}
487 496
488static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, 497static void
489 struct sh_mobile_meram_cfg *cfg) 498sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
490{ 499{
491 struct sh_mobile_meram_priv *priv; 500 struct sh_mobile_meram_fb_cache *cache = data;
492 501 struct sh_mobile_meram_priv *priv = pdata->priv;
493 if (!pdata || !pdata->priv || !cfg)
494 return -EINVAL;
495
496 priv = pdata->priv;
497 502
498 mutex_lock(&priv->lock); 503 mutex_lock(&priv->lock);
499 504
500 /* deinit & unmark */ 505 /* deinit & free */
501 if (is_nvcolor(cfg->pixelformat)) { 506 meram_deinit(priv, &cache->planes[0]);
502 meram_deinit(priv, &cfg->icb[1]); 507 if (cache->nplanes == 2)
503 meram_unmark(priv, &cfg->icb[1]); 508 meram_deinit(priv, &cache->planes[1]);
504 }
505 meram_deinit(priv, &cfg->icb[0]);
506 meram_unmark(priv, &cfg->icb[0]);
507 509
508 mutex_unlock(&priv->lock); 510 meram_free(priv, cache);
509 511
510 return 0; 512 mutex_unlock(&priv->lock);
511} 513}
512 514
513static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, 515static void
514 struct sh_mobile_meram_cfg *cfg, 516sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
515 unsigned long base_addr_y, 517 unsigned long base_addr_y, unsigned long base_addr_c,
516 unsigned long base_addr_c, 518 unsigned long *icb_addr_y, unsigned long *icb_addr_c)
517 unsigned long *icb_addr_y,
518 unsigned long *icb_addr_c)
519{ 519{
520 struct sh_mobile_meram_priv *priv; 520 struct sh_mobile_meram_fb_cache *cache = data;
521 521 struct sh_mobile_meram_priv *priv = pdata->priv;
522 if (!pdata || !pdata->priv || !cfg)
523 return -EINVAL;
524
525 priv = pdata->priv;
526 522
527 mutex_lock(&priv->lock); 523 mutex_lock(&priv->lock);
528 524
529 meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c); 525 meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
530 meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c); 526 meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
531 527
532 mutex_unlock(&priv->lock); 528 mutex_unlock(&priv->lock);
533
534 return 0;
535} 529}
536 530
537static int sh_mobile_meram_runtime_suspend(struct device *dev) 531static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
532 .module = THIS_MODULE,
533 .meram_register = sh_mobile_meram_register,
534 .meram_unregister = sh_mobile_meram_unregister,
535 .meram_update = sh_mobile_meram_update,
536};
537
538/* -----------------------------------------------------------------------------
539 * Power management
540 */
541
542static int sh_mobile_meram_suspend(struct device *dev)
538{ 543{
539 struct platform_device *pdev = to_platform_device(dev); 544 struct platform_device *pdev = to_platform_device(dev);
540 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); 545 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
541 int k, j; 546 unsigned int i, j;
542 547
543 for (k = 0; k < CMN_REGS_SIZE; k++) 548 for (i = 0; i < MERAM_REGS_SIZE; i++)
544 priv->cmn_saved_regs[k] = meram_read_reg(priv->base, 549 priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
545 common_regs[k]);
546 550
547 for (j = 0; j < 32; j++) { 551 for (i = 0; i < 32; i++) {
548 if (!test_bit(j, &priv->used_icb)) 552 if (!test_bit(i, &priv->used_icb))
549 continue; 553 continue;
550 for (k = 0; k < ICB_REGS_SIZE; k++) { 554 for (j = 0; j < ICB_REGS_SIZE; j++) {
551 priv->icb_saved_regs[j * ICB_REGS_SIZE + k] = 555 priv->icbs[i].regs[j] =
552 meram_read_icb(priv->base, j, icb_regs[k]); 556 meram_read_icb(priv->base, i, icb_regs[j]);
553 /* Reset ICB on resume */ 557 /* Reset ICB on resume */
554 if (icb_regs[k] == MExxCTL) 558 if (icb_regs[j] == MExxCTL)
555 priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |= 559 priv->icbs[i].regs[j] |=
556 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF; 560 MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
557 } 561 }
558 } 562 }
559 return 0; 563 return 0;
560} 564}
561 565
562static int sh_mobile_meram_runtime_resume(struct device *dev) 566static int sh_mobile_meram_resume(struct device *dev)
563{ 567{
564 struct platform_device *pdev = to_platform_device(dev); 568 struct platform_device *pdev = to_platform_device(dev);
565 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); 569 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
566 int k, j; 570 unsigned int i, j;
567 571
568 for (j = 0; j < 32; j++) { 572 for (i = 0; i < 32; i++) {
569 if (!test_bit(j, &priv->used_icb)) 573 if (!test_bit(i, &priv->used_icb))
570 continue; 574 continue;
571 for (k = 0; k < ICB_REGS_SIZE; k++) { 575 for (j = 0; j < ICB_REGS_SIZE; j++)
572 meram_write_icb(priv->base, j, icb_regs[k], 576 meram_write_icb(priv->base, i, icb_regs[j],
573 priv->icb_saved_regs[j * ICB_REGS_SIZE + k]); 577 priv->icbs[i].regs[j]);
574 }
575 } 578 }
576 579
577 for (k = 0; k < CMN_REGS_SIZE; k++) 580 for (i = 0; i < MERAM_REGS_SIZE; i++)
578 meram_write_reg(priv->base, common_regs[k], 581 meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
579 priv->cmn_saved_regs[k]);
580 return 0; 582 return 0;
581} 583}
582 584
583static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = { 585static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops,
584 .runtime_suspend = sh_mobile_meram_runtime_suspend, 586 sh_mobile_meram_suspend,
585 .runtime_resume = sh_mobile_meram_runtime_resume, 587 sh_mobile_meram_resume, NULL);
586};
587
588static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
589 .module = THIS_MODULE,
590 .meram_register = sh_mobile_meram_register,
591 .meram_unregister = sh_mobile_meram_unregister,
592 .meram_update = sh_mobile_meram_update,
593};
594 588
595/* 589/* -----------------------------------------------------------------------------
596 * initialize MERAM 590 * Probe/remove and driver init/exit
597 */ 591 */
598 592
599static int sh_mobile_meram_remove(struct platform_device *pdev);
600
601static int __devinit sh_mobile_meram_probe(struct platform_device *pdev) 593static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
602{ 594{
603 struct sh_mobile_meram_priv *priv; 595 struct sh_mobile_meram_priv *priv;
604 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data; 596 struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
605 struct resource *res; 597 struct resource *regs;
598 struct resource *meram;
599 unsigned int i;
606 int error; 600 int error;
607 601
608 if (!pdata) { 602 if (!pdata) {
@@ -610,8 +604,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
610 return -EINVAL; 604 return -EINVAL;
611 } 605 }
612 606
613 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 607 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
614 if (!res) { 608 meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
609 if (regs == NULL || meram == NULL) {
615 dev_err(&pdev->dev, "cannot get platform resources\n"); 610 dev_err(&pdev->dev, "cannot get platform resources\n");
616 return -ENOENT; 611 return -ENOENT;
617 } 612 }
@@ -622,32 +617,74 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
622 return -ENOMEM; 617 return -ENOMEM;
623 } 618 }
624 619
625 platform_set_drvdata(pdev, priv); 620 /* Initialize private data. */
626
627 /* initialize private data */
628 mutex_init(&priv->lock); 621 mutex_init(&priv->lock);
629 priv->base = ioremap_nocache(res->start, resource_size(res)); 622 priv->used_icb = pdata->reserved_icbs;
623
624 for (i = 0; i < MERAM_ICB_NUM; ++i)
625 priv->icbs[i].index = i;
626
627 pdata->ops = &sh_mobile_meram_ops;
628 pdata->priv = priv;
629 pdata->pdev = pdev;
630
631 /* Request memory regions and remap the registers. */
632 if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
633 dev_err(&pdev->dev, "MERAM registers region already claimed\n");
634 error = -EBUSY;
635 goto err_req_regs;
636 }
637
638 if (!request_mem_region(meram->start, resource_size(meram),
639 pdev->name)) {
640 dev_err(&pdev->dev, "MERAM memory region already claimed\n");
641 error = -EBUSY;
642 goto err_req_meram;
643 }
644
645 priv->base = ioremap_nocache(regs->start, resource_size(regs));
630 if (!priv->base) { 646 if (!priv->base) {
631 dev_err(&pdev->dev, "ioremap failed\n"); 647 dev_err(&pdev->dev, "ioremap failed\n");
632 error = -EFAULT; 648 error = -EFAULT;
633 goto err; 649 goto err_ioremap;
634 } 650 }
635 pdata->ops = &sh_mobile_meram_ops; 651
636 pdata->priv = priv; 652 priv->meram = meram->start;
637 pdata->pdev = pdev; 653
654 /* Create and initialize the MERAM memory pool. */
655 priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
656 if (priv->pool == NULL) {
657 error = -ENOMEM;
658 goto err_genpool;
659 }
660
661 error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
662 -1);
663 if (error < 0)
664 goto err_genpool;
638 665
639 /* initialize ICB addressing mode */ 666 /* initialize ICB addressing mode */
640 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1) 667 if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
641 meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1); 668 meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
642 669
670 platform_set_drvdata(pdev, priv);
643 pm_runtime_enable(&pdev->dev); 671 pm_runtime_enable(&pdev->dev);
644 672
645 dev_info(&pdev->dev, "sh_mobile_meram initialized."); 673 dev_info(&pdev->dev, "sh_mobile_meram initialized.");
646 674
647 return 0; 675 return 0;
648 676
649err: 677err_genpool:
650 sh_mobile_meram_remove(pdev); 678 if (priv->pool)
679 gen_pool_destroy(priv->pool);
680 iounmap(priv->base);
681err_ioremap:
682 release_mem_region(meram->start, resource_size(meram));
683err_req_meram:
684 release_mem_region(regs->start, resource_size(regs));
685err_req_regs:
686 mutex_destroy(&priv->lock);
687 kfree(priv);
651 688
652 return error; 689 return error;
653} 690}
@@ -656,11 +693,16 @@ err:
656static int sh_mobile_meram_remove(struct platform_device *pdev) 693static int sh_mobile_meram_remove(struct platform_device *pdev)
657{ 694{
658 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev); 695 struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
696 struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
697 struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
659 698
660 pm_runtime_disable(&pdev->dev); 699 pm_runtime_disable(&pdev->dev);
661 700
662 if (priv->base) 701 gen_pool_destroy(priv->pool);
663 iounmap(priv->base); 702
703 iounmap(priv->base);
704 release_mem_region(meram->start, resource_size(meram));
705 release_mem_region(regs->start, resource_size(regs));
664 706
665 mutex_destroy(&priv->lock); 707 mutex_destroy(&priv->lock);
666 708