Blame view
drivers/video/sh_mobile_lcdcfb.c
45.5 KB
cfb4f5d17 fbdev: SuperH Mob... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * SuperH Mobile LCDC Framebuffer * * Copyright (c) 2008 Magnus Damm * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/mm.h> |
cfb4f5d17 fbdev: SuperH Mob... |
15 |
#include <linux/clk.h> |
0246c4712 video: Runtime PM... |
16 |
#include <linux/pm_runtime.h> |
cfb4f5d17 fbdev: SuperH Mob... |
17 18 |
#include <linux/platform_device.h> #include <linux/dma-mapping.h> |
8564557a0 video: sh_mobile_... |
19 |
#include <linux/interrupt.h> |
edd153a3e fbdev: sh_mobile_... |
20 |
#include <linux/videodev2.h> |
1c6a307a5 sh: LCDC dcache f... |
21 |
#include <linux/vmalloc.h> |
40331b21f video: sh_mobile_... |
22 |
#include <linux/ioctl.h> |
5a0e3ad6a include cleanup: ... |
23 |
#include <linux/slab.h> |
dd210503b fbdev: sh_mobile_... |
24 |
#include <linux/console.h> |
3b0fd9d75 fbdev: sh_mobile_... |
25 26 |
#include <linux/backlight.h> #include <linux/gpio.h> |
355b200ba video: Add module... |
27 |
#include <linux/module.h> |
225c9a8d1 video: sh_mobile_... |
28 |
#include <video/sh_mobile_lcdc.h> |
8a20974f0 fbdev: sh_mobile_... |
29 |
#include <video/sh_mobile_meram.h> |
60063497a atomic: use <linu... |
30 |
#include <linux/atomic.h> |
cfb4f5d17 fbdev: SuperH Mob... |
31 |
|
6de9edd5b fbdev: sh_mobile_... |
32 |
#include "sh_mobile_lcdcfb.h" |
a6f15ade9 video: sh_mobile_... |
33 34 |
#define SIDE_B_OFFSET 0x1000 #define MIRROR_OFFSET 0x2000 |
cfb4f5d17 fbdev: SuperH Mob... |
35 |
|
d2ecbab59 fbdev: sh_mobile_... |
36 37 |
#define MAX_XRES 1920 #define MAX_YRES 1080 |
cfb4f5d17 fbdev: SuperH Mob... |
38 |
|
0246c4712 video: Runtime PM... |
39 |
static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { |
cfb4f5d17 fbdev: SuperH Mob... |
40 41 42 43 44 45 46 |
[LDDCKPAT1R] = 0x400, [LDDCKPAT2R] = 0x404, [LDMT1R] = 0x418, [LDMT2R] = 0x41c, [LDMT3R] = 0x420, [LDDFR] = 0x424, [LDSM1R] = 0x428, |
8564557a0 video: sh_mobile_... |
47 |
[LDSM2R] = 0x42c, |
cfb4f5d17 fbdev: SuperH Mob... |
48 |
[LDSA1R] = 0x430, |
53b503143 fbdev: sh_mobile_... |
49 |
[LDSA2R] = 0x434, |
cfb4f5d17 fbdev: SuperH Mob... |
50 51 52 53 54 55 |
[LDMLSR] = 0x438, [LDHCNR] = 0x448, [LDHSYNR] = 0x44c, [LDVLNR] = 0x450, [LDVSYNR] = 0x454, [LDPMR] = 0x460, |
6011bdeaa fbdev: sh-mobile:... |
56 |
[LDHAJR] = 0x4a0, |
cfb4f5d17 fbdev: SuperH Mob... |
57 |
}; |
0246c4712 video: Runtime PM... |
58 |
static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { |
cfb4f5d17 fbdev: SuperH Mob... |
59 60 61 62 63 64 65 |
[LDDCKPAT1R] = 0x408, [LDDCKPAT2R] = 0x40c, [LDMT1R] = 0x600, [LDMT2R] = 0x604, [LDMT3R] = 0x608, [LDDFR] = 0x60c, [LDSM1R] = 0x610, |
8564557a0 video: sh_mobile_... |
66 |
[LDSM2R] = 0x614, |
cfb4f5d17 fbdev: SuperH Mob... |
67 68 69 70 71 72 73 74 |
[LDSA1R] = 0x618, [LDMLSR] = 0x620, [LDHCNR] = 0x624, [LDHSYNR] = 0x628, [LDVLNR] = 0x62c, [LDVSYNR] = 0x630, [LDPMR] = 0x63c, }; |
c44f9f76d fbdev: sh_mobile_... |
75 76 77 78 |
static const struct fb_videomode default_720p = { .name = "HDMI 720p", .xres = 1280, .yres = 720, |
5ae0cf82d fbdev: sh_mobile_... |
79 80 81 |
.left_margin = 220, .right_margin = 110, .hsync_len = 40, |
c44f9f76d fbdev: sh_mobile_... |
82 83 84 85 86 87 |
.upper_margin = 20, .lower_margin = 5, .vsync_len = 5, .pixclock = 13468, |
5ae0cf82d fbdev: sh_mobile_... |
88 |
.refresh = 60, |
c44f9f76d fbdev: sh_mobile_... |
89 |
.sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, |
0246c4712 video: Runtime PM... |
90 91 92 93 94 95 96 97 98 99 |
}; struct sh_mobile_lcdc_priv { void __iomem *base; int irq; atomic_t hw_usecnt; struct device *dev; struct clk *dot_clk; unsigned long lddckr; struct sh_mobile_lcdc_chan ch[2]; |
6011bdeaa fbdev: sh-mobile:... |
100 |
struct notifier_block notifier; |
0246c4712 video: Runtime PM... |
101 |
int started; |
edd153a3e fbdev: sh_mobile_... |
102 |
int forced_fourcc; /* 2 channel LCDC must share fourcc setting */ |
7caa4342c sh_mobile_meram: ... |
103 |
struct sh_mobile_meram_info *meram_dev; |
0246c4712 video: Runtime PM... |
104 |
}; |
a6f15ade9 video: sh_mobile_... |
105 106 107 108 109 110 111 112 113 |
static bool banked(int reg_nr) { switch (reg_nr) { case LDMT1R: case LDMT2R: case LDMT3R: case LDDFR: case LDSM1R: case LDSA1R: |
53b503143 fbdev: sh_mobile_... |
114 |
case LDSA2R: |
a6f15ade9 video: sh_mobile_... |
115 116 117 118 119 120 121 122 123 |
case LDMLSR: case LDHCNR: case LDHSYNR: case LDVLNR: case LDVSYNR: return true; } return false; } |
cfb4f5d17 fbdev: SuperH Mob... |
124 125 126 127 |
static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, int reg_nr, unsigned long data) { iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]); |
a6f15ade9 video: sh_mobile_... |
128 129 130 131 132 133 134 135 136 137 |
if (banked(reg_nr)) iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] + SIDE_B_OFFSET); } static void lcdc_write_chan_mirror(struct sh_mobile_lcdc_chan *chan, int reg_nr, unsigned long data) { iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] + MIRROR_OFFSET); |
cfb4f5d17 fbdev: SuperH Mob... |
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
} static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, int reg_nr) { return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); } static void lcdc_write(struct sh_mobile_lcdc_priv *priv, unsigned long reg_offs, unsigned long data) { iowrite32(data, priv->base + reg_offs); } static unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv, unsigned long reg_offs) { return ioread32(priv->base + reg_offs); } static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv, unsigned long reg_offs, unsigned long mask, unsigned long until) { while ((lcdc_read(priv, reg_offs) & mask) != until) cpu_relax(); } static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) { return chan->cfg.chan == LCDC_CHAN_SUBLCD; } static void lcdc_sys_write_index(void *handle, unsigned long data) { struct sh_mobile_lcdc_chan *ch = handle; |
ce1c0b087 fbdev: sh_mobile_... |
174 175 176 177 178 |
lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); |
cfb4f5d17 fbdev: SuperH Mob... |
179 180 181 182 183 |
} static void lcdc_sys_write_data(void *handle, unsigned long data) { struct sh_mobile_lcdc_chan *ch = handle; |
ce1c0b087 fbdev: sh_mobile_... |
184 185 186 187 188 |
lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT | LDDWDxR_RSW); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); |
cfb4f5d17 fbdev: SuperH Mob... |
189 190 191 192 193 |
} static unsigned long lcdc_sys_read_data(void *handle) { struct sh_mobile_lcdc_chan *ch = handle; |
ce1c0b087 fbdev: sh_mobile_... |
194 195 196 197 |
lcdc_write(ch->lcdc, _LDDRDR, LDDRDR_RSR); lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); lcdc_write(ch->lcdc, _LDDRAR, LDDRAR_RA | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); |
cfb4f5d17 fbdev: SuperH Mob... |
198 |
udelay(1); |
ce1c0b087 fbdev: sh_mobile_... |
199 |
lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0); |
cfb4f5d17 fbdev: SuperH Mob... |
200 |
|
ce1c0b087 fbdev: sh_mobile_... |
201 |
return lcdc_read(ch->lcdc, _LDDRDR) & LDDRDR_DRD_MASK; |
cfb4f5d17 fbdev: SuperH Mob... |
202 203 204 205 206 207 208 |
} struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { lcdc_sys_write_index, lcdc_sys_write_data, lcdc_sys_read_data, }; |
edd153a3e fbdev: sh_mobile_... |
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var) { if (var->grayscale > 1) return var->grayscale; switch (var->bits_per_pixel) { case 16: return V4L2_PIX_FMT_RGB565; case 24: return V4L2_PIX_FMT_BGR24; case 32: return V4L2_PIX_FMT_BGR32; default: return 0; } } static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var) { return var->grayscale > 1; } static bool sh_mobile_format_is_yuv(const struct fb_var_screeninfo *var) { if (var->grayscale <= 1) return false; switch (var->grayscale) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV24: case V4L2_PIX_FMT_NV42: return true; default: return false; } } |
8564557a0 video: sh_mobile_... |
249 250 |
static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) { |
0246c4712 video: Runtime PM... |
251 |
if (atomic_inc_and_test(&priv->hw_usecnt)) { |
8564557a0 video: sh_mobile_... |
252 253 |
if (priv->dot_clk) clk_enable(priv->dot_clk); |
f1ad90da5 fbdev: sh_mobile_... |
254 |
pm_runtime_get_sync(priv->dev); |
ec19b9e0f fbdev: sh_mobile_... |
255 256 |
if (priv->meram_dev && priv->meram_dev->pdev) pm_runtime_get_sync(&priv->meram_dev->pdev->dev); |
8564557a0 video: sh_mobile_... |
257 258 259 260 261 |
} } static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) { |
0246c4712 video: Runtime PM... |
262 |
if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { |
ec19b9e0f fbdev: sh_mobile_... |
263 264 |
if (priv->meram_dev && priv->meram_dev->pdev) pm_runtime_put_sync(&priv->meram_dev->pdev->dev); |
0246c4712 video: Runtime PM... |
265 |
pm_runtime_put(priv->dev); |
f1ad90da5 fbdev: sh_mobile_... |
266 267 |
if (priv->dot_clk) clk_disable(priv->dot_clk); |
8564557a0 video: sh_mobile_... |
268 269 |
} } |
8564557a0 video: sh_mobile_... |
270 |
|
1c6a307a5 sh: LCDC dcache f... |
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
static int sh_mobile_lcdc_sginit(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT; struct page *page; int nr_pages = 0; sg_init_table(ch->sglist, nr_pages_max); list_for_each_entry(page, pagelist, lru) sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0); return nr_pages; } |
8564557a0 video: sh_mobile_... |
286 287 288 289 |
static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; |
ef61aae4d sh: add a start_t... |
290 |
struct sh_mobile_lcdc_board_cfg *bcfg = &ch->cfg.board_cfg; |
8564557a0 video: sh_mobile_... |
291 292 293 |
/* enable clocks before accessing hardware */ sh_mobile_lcdc_clk_on(ch->lcdc); |
5c1a56b5f video: sh_mobile_... |
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
/* * It's possible to get here without anything on the pagelist via * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync() * invocation. In the former case, the acceleration routines are * stepped in to when using the framebuffer console causing the * workqueue to be scheduled without any dirty pages on the list. * * Despite this, a panel update is still needed given that the * acceleration routines have their own methods for writing in * that still need to be updated. * * The fsync() and empty pagelist case could be optimized for, * but we don't bother, as any application exhibiting such * behaviour is fundamentally broken anyways. */ if (!list_empty(pagelist)) { unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); /* trigger panel update */ dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); |
ef61aae4d sh: add a start_t... |
314 315 316 |
if (bcfg->start_transfer) bcfg->start_transfer(bcfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); |
ce1c0b087 fbdev: sh_mobile_... |
317 |
lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); |
5c1a56b5f video: sh_mobile_... |
318 |
dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); |
ef61aae4d sh: add a start_t... |
319 320 321 322 |
} else { if (bcfg->start_transfer) bcfg->start_transfer(bcfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); |
ce1c0b087 fbdev: sh_mobile_... |
323 |
lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG); |
ef61aae4d sh: add a start_t... |
324 |
} |
8564557a0 video: sh_mobile_... |
325 326 327 328 329 330 331 332 333 334 335 336 337 |
} static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) { struct fb_deferred_io *fbdefio = info->fbdefio; if (fbdefio) schedule_delayed_work(&info->deferred_work, fbdefio->delay); } static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) { struct sh_mobile_lcdc_priv *priv = data; |
2feb075a3 video: sh_mobile_... |
338 |
struct sh_mobile_lcdc_chan *ch; |
9dd38819c video: sh_mobile_... |
339 |
unsigned long ldintr; |
2feb075a3 video: sh_mobile_... |
340 341 |
int is_sub; int k; |
8564557a0 video: sh_mobile_... |
342 |
|
dc48665fa fbdev: sh_mobile_... |
343 344 345 |
/* Acknowledge interrupts and disable further VSYNC End IRQs. */ ldintr = lcdc_read(priv, _LDINTR); lcdc_write(priv, _LDINTR, (ldintr ^ LDINTR_STATUS_MASK) & ~LDINTR_VEE); |
8564557a0 video: sh_mobile_... |
346 |
|
2feb075a3 video: sh_mobile_... |
347 |
/* figure out if this interrupt is for main or sub lcd */ |
ce1c0b087 fbdev: sh_mobile_... |
348 |
is_sub = (lcdc_read(priv, _LDSR) & LDSR_MSS) ? 1 : 0; |
2feb075a3 video: sh_mobile_... |
349 |
|
9dd38819c video: sh_mobile_... |
350 |
/* wake up channel and disable clocks */ |
2feb075a3 video: sh_mobile_... |
351 352 353 354 355 |
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!ch->enabled) continue; |
dc48665fa fbdev: sh_mobile_... |
356 |
/* Frame End */ |
9dd38819c video: sh_mobile_... |
357 358 359 360 |
if (ldintr & LDINTR_FS) { if (is_sub == lcdc_chan_is_sublcd(ch)) { ch->frame_end = 1; wake_up(&ch->frame_end_wait); |
2feb075a3 video: sh_mobile_... |
361 |
|
9dd38819c video: sh_mobile_... |
362 363 364 365 366 |
sh_mobile_lcdc_clk_off(priv); } } /* VSYNC End */ |
40331b21f video: sh_mobile_... |
367 368 |
if (ldintr & LDINTR_VES) complete(&ch->vsync_completion); |
2feb075a3 video: sh_mobile_... |
369 |
} |
8564557a0 video: sh_mobile_... |
370 371 |
return IRQ_HANDLED; } |
cfb4f5d17 fbdev: SuperH Mob... |
372 373 374 375 376 377 378 379 |
static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, int start) { unsigned long tmp = lcdc_read(priv, _LDCNT2R); int k; /* start or stop the lcdc */ if (start) |
ce1c0b087 fbdev: sh_mobile_... |
380 |
lcdc_write(priv, _LDCNT2R, tmp | LDCNT2R_DO); |
cfb4f5d17 fbdev: SuperH Mob... |
381 |
else |
ce1c0b087 fbdev: sh_mobile_... |
382 |
lcdc_write(priv, _LDCNT2R, tmp & ~LDCNT2R_DO); |
cfb4f5d17 fbdev: SuperH Mob... |
383 384 385 386 387 |
/* wait until power is applied/stopped on all channels */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled) while (1) { |
ce1c0b087 fbdev: sh_mobile_... |
388 389 390 |
tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & LDPMR_LPS; if (start && tmp == LDPMR_LPS) |
cfb4f5d17 fbdev: SuperH Mob... |
391 392 393 394 395 396 397 398 399 |
break; if (!start && tmp == 0) break; cpu_relax(); } if (!start) lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ } |
6011bdeaa fbdev: sh-mobile:... |
400 401 |
static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) { |
1c120deb6 fbdev: sh_mobile_... |
402 403 |
struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var; unsigned long h_total, hsync_pos, display_h_total; |
6011bdeaa fbdev: sh-mobile:... |
404 405 406 |
u32 tmp; tmp = ch->ldmt1r_value; |
ce1c0b087 fbdev: sh_mobile_... |
407 408 409 410 411 412 413 |
tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL; tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL; tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0; |
6011bdeaa fbdev: sh-mobile:... |
414 415 416 417 418 419 420 |
lcdc_write_chan(ch, LDMT1R, tmp); /* setup SYS bus */ lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); /* horizontal configuration */ |
1c120deb6 fbdev: sh_mobile_... |
421 422 |
h_total = display_var->xres + display_var->hsync_len + display_var->left_margin + display_var->right_margin; |
6011bdeaa fbdev: sh-mobile:... |
423 |
tmp = h_total / 8; /* HTCN */ |
1c120deb6 fbdev: sh_mobile_... |
424 |
tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */ |
6011bdeaa fbdev: sh-mobile:... |
425 |
lcdc_write_chan(ch, LDHCNR, tmp); |
1c120deb6 fbdev: sh_mobile_... |
426 |
hsync_pos = display_var->xres + display_var->right_margin; |
6011bdeaa fbdev: sh-mobile:... |
427 |
tmp = hsync_pos / 8; /* HSYNP */ |
1c120deb6 fbdev: sh_mobile_... |
428 |
tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */ |
6011bdeaa fbdev: sh-mobile:... |
429 430 431 |
lcdc_write_chan(ch, LDHSYNR, tmp); /* vertical configuration */ |
1c120deb6 fbdev: sh_mobile_... |
432 433 434 |
tmp = display_var->yres + display_var->vsync_len + display_var->upper_margin + display_var->lower_margin; /* VTLN */ tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */ |
6011bdeaa fbdev: sh-mobile:... |
435 |
lcdc_write_chan(ch, LDVLNR, tmp); |
1c120deb6 fbdev: sh_mobile_... |
436 437 |
tmp = display_var->yres + display_var->lower_margin; /* VSYNP */ tmp |= display_var->vsync_len << 16; /* VSYNW */ |
6011bdeaa fbdev: sh-mobile:... |
438 439 440 |
lcdc_write_chan(ch, LDVSYNR, tmp); /* Adjust horizontal synchronisation for HDMI */ |
1c120deb6 fbdev: sh_mobile_... |
441 442 443 444 445 |
display_h_total = display_var->xres + display_var->hsync_len + display_var->left_margin + display_var->right_margin; tmp = ((display_var->xres & 7) << 24) | ((display_h_total & 7) << 16) | ((display_var->hsync_len & 7) << 8) | |
41e583c22 fbdev: sh_mobile_... |
446 |
(hsync_pos & 7); |
6011bdeaa fbdev: sh-mobile:... |
447 448 |
lcdc_write_chan(ch, LDHAJR, tmp); } |
9a217e344 fbdev: sh_mobile_... |
449 450 451 452 453 454 455 456 |
/* * __sh_mobile_lcdc_start - Configure and tart the LCDC * @priv: LCDC device * * Configure all enabled channels and start the LCDC device. All external * devices (clocks, MERAM, panels, ...) are not touched by this function. */ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) |
cfb4f5d17 fbdev: SuperH Mob... |
457 458 |
{ struct sh_mobile_lcdc_chan *ch; |
cfb4f5d17 fbdev: SuperH Mob... |
459 |
unsigned long tmp; |
9a217e344 fbdev: sh_mobile_... |
460 |
int k, m; |
8564557a0 video: sh_mobile_... |
461 |
|
9a217e344 fbdev: sh_mobile_... |
462 463 464 465 |
/* Enable LCDC channels. Read data from external memory, avoid using the * BEU for now. */ lcdc_write(priv, _LDCNT2R, priv->ch[0].enabled | priv->ch[1].enabled); |
cfb4f5d17 fbdev: SuperH Mob... |
466 |
|
9a217e344 fbdev: sh_mobile_... |
467 |
/* Stop the LCDC first and disable all interrupts. */ |
cfb4f5d17 fbdev: SuperH Mob... |
468 |
sh_mobile_lcdc_start_stop(priv, 0); |
9a217e344 fbdev: sh_mobile_... |
469 |
lcdc_write(priv, _LDINTR, 0); |
cfb4f5d17 fbdev: SuperH Mob... |
470 |
|
9a217e344 fbdev: sh_mobile_... |
471 |
/* Configure power supply, dot clocks and start them. */ |
cfb4f5d17 fbdev: SuperH Mob... |
472 473 474 |
tmp = priv->lddckr; for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; |
9a217e344 fbdev: sh_mobile_... |
475 |
if (!ch->enabled) |
cfb4f5d17 fbdev: SuperH Mob... |
476 |
continue; |
9a217e344 fbdev: sh_mobile_... |
477 478 |
/* Power supply */ lcdc_write_chan(ch, LDPMR, 0); |
cfb4f5d17 fbdev: SuperH Mob... |
479 480 481 |
m = ch->cfg.clock_divider; if (!m) continue; |
505c7de51 fbdev: sh_mobile_... |
482 483 484 485 486 |
/* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider * denominator. */ lcdc_write_chan(ch, LDDCKPAT1R, 0); lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); |
cfb4f5d17 fbdev: SuperH Mob... |
487 |
if (m == 1) |
ce1c0b087 fbdev: sh_mobile_... |
488 |
m = LDDCKR_MOSEL; |
cfb4f5d17 fbdev: SuperH Mob... |
489 |
tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); |
cfb4f5d17 fbdev: SuperH Mob... |
490 491 492 |
} lcdc_write(priv, _LDDCKR, tmp); |
cfb4f5d17 fbdev: SuperH Mob... |
493 494 |
lcdc_write(priv, _LDDCKSTPR, 0); lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); |
9a217e344 fbdev: sh_mobile_... |
495 |
/* Setup geometry, format, frame buffer memory and operation mode. */ |
cfb4f5d17 fbdev: SuperH Mob... |
496 497 |
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; |
cfb4f5d17 fbdev: SuperH Mob... |
498 499 |
if (!ch->enabled) continue; |
6011bdeaa fbdev: sh-mobile:... |
500 |
sh_mobile_lcdc_geometry(ch); |
cfb4f5d17 fbdev: SuperH Mob... |
501 |
|
edd153a3e fbdev: sh_mobile_... |
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 |
switch (sh_mobile_format_fourcc(&ch->info->var)) { case V4L2_PIX_FMT_RGB565: tmp = LDDFR_PKF_RGB16; break; case V4L2_PIX_FMT_BGR24: tmp = LDDFR_PKF_RGB24; break; case V4L2_PIX_FMT_BGR32: tmp = LDDFR_PKF_ARGB32; break; case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: tmp = LDDFR_CC | LDDFR_YF_420; break; case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: tmp = LDDFR_CC | LDDFR_YF_422; break; case V4L2_PIX_FMT_NV24: case V4L2_PIX_FMT_NV42: tmp = LDDFR_CC | LDDFR_YF_444; break; } if (sh_mobile_format_is_yuv(&ch->info->var)) { switch (ch->info->var.colorspace) { case V4L2_COLORSPACE_REC709: tmp |= LDDFR_CF1; |
53b503143 fbdev: sh_mobile_... |
530 |
break; |
edd153a3e fbdev: sh_mobile_... |
531 532 |
case V4L2_COLORSPACE_JPEG: tmp |= LDDFR_CF0; |
53b503143 fbdev: sh_mobile_... |
533 534 |
break; } |
417d48274 fbdev: sh_mobile_... |
535 |
} |
7caa4342c sh_mobile_meram: ... |
536 |
|
9a217e344 fbdev: sh_mobile_... |
537 538 539 |
lcdc_write_chan(ch, LDDFR, tmp); lcdc_write_chan(ch, LDMLSR, ch->pitch); lcdc_write_chan(ch, LDSA1R, ch->base_addr_y); |
edd153a3e fbdev: sh_mobile_... |
540 |
if (sh_mobile_format_is_yuv(&ch->info->var)) |
9a217e344 fbdev: sh_mobile_... |
541 |
lcdc_write_chan(ch, LDSA2R, ch->base_addr_c); |
7caa4342c sh_mobile_meram: ... |
542 |
|
9a217e344 fbdev: sh_mobile_... |
543 544 545 546 547 548 549 550 551 552 553 554 |
/* When using deferred I/O mode, configure the LCDC for one-shot * operation and enable the frame end interrupt. Otherwise use * continuous read mode. */ if (ch->ldmt1r_value & LDMT1R_IFM && ch->cfg.sys_bus_cfg.deferred_io_msec) { lcdc_write_chan(ch, LDSM1R, LDSM1R_OS); lcdc_write(priv, _LDINTR, LDINTR_FE); } else { lcdc_write_chan(ch, LDSM1R, 0); } } |
7caa4342c sh_mobile_meram: ... |
555 |
|
9a217e344 fbdev: sh_mobile_... |
556 |
/* Word and long word swap. */ |
edd153a3e fbdev: sh_mobile_... |
557 558 559 560 561 562 563 564 565 566 567 |
switch (sh_mobile_format_fourcc(&priv->ch[0].info->var)) { case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV61: case V4L2_PIX_FMT_NV42: tmp = LDDDSR_LS | LDDDSR_WS; break; case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV24: |
9a217e344 fbdev: sh_mobile_... |
568 |
tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; |
edd153a3e fbdev: sh_mobile_... |
569 570 571 572 573 |
break; case V4L2_PIX_FMT_BGR32: default: tmp = LDDDSR_LS; break; |
9a217e344 fbdev: sh_mobile_... |
574 575 |
} lcdc_write(priv, _LDDDSR, tmp); |
7caa4342c sh_mobile_meram: ... |
576 |
|
9a217e344 fbdev: sh_mobile_... |
577 578 579 580 581 |
/* Enable the display output. */ lcdc_write(priv, _LDCNT1R, LDCNT1R_DE); sh_mobile_lcdc_start_stop(priv, 1); priv->started = 1; } |
cfb4f5d17 fbdev: SuperH Mob... |
582 |
|
9a217e344 fbdev: sh_mobile_... |
583 584 585 586 587 588 589 590 |
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_meram_info *mdev = priv->meram_dev; struct sh_mobile_lcdc_board_cfg *board_cfg; struct sh_mobile_lcdc_chan *ch; unsigned long tmp; int ret; int k; |
cfb4f5d17 fbdev: SuperH Mob... |
591 |
|
9a217e344 fbdev: sh_mobile_... |
592 593 594 595 596 |
/* enable clocks before accessing the hardware */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { if (priv->ch[k].enabled) sh_mobile_lcdc_clk_on(priv); } |
8564557a0 video: sh_mobile_... |
597 |
|
9a217e344 fbdev: sh_mobile_... |
598 599 600 |
/* reset */ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LDCNT2R_BR); lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0); |
8564557a0 video: sh_mobile_... |
601 |
|
9a217e344 fbdev: sh_mobile_... |
602 603 |
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; |
8564557a0 video: sh_mobile_... |
604 |
|
9a217e344 fbdev: sh_mobile_... |
605 606 607 608 609 610 611 612 613 |
if (!ch->enabled) continue; board_cfg = &ch->cfg.board_cfg; if (board_cfg->setup_sys) { ret = board_cfg->setup_sys(board_cfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); if (ret) return ret; |
8564557a0 video: sh_mobile_... |
614 |
} |
cfb4f5d17 fbdev: SuperH Mob... |
615 |
} |
9a217e344 fbdev: sh_mobile_... |
616 617 618 619 |
/* Compute frame buffer base address and pitch for each channel. */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { struct sh_mobile_meram_cfg *cfg; int pixelformat; |
cfb4f5d17 fbdev: SuperH Mob... |
620 |
|
9a217e344 fbdev: sh_mobile_... |
621 622 623 |
ch = &priv->ch[k]; if (!ch->enabled) continue; |
cfb4f5d17 fbdev: SuperH Mob... |
624 |
|
9a217e344 fbdev: sh_mobile_... |
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 |
ch->base_addr_y = ch->info->fix.smem_start; ch->base_addr_c = ch->base_addr_y + ch->info->var.xres * ch->info->var.yres_virtual; ch->pitch = ch->info->fix.line_length; /* Enable MERAM if possible. */ cfg = ch->cfg.meram_cfg; if (mdev == NULL || mdev->ops == NULL || cfg == NULL) continue; /* we need to de-init configured ICBs before we can * re-initialize them. */ if (ch->meram_enabled) { mdev->ops->meram_unregister(mdev, cfg); ch->meram_enabled = 0; } |
edd153a3e fbdev: sh_mobile_... |
643 644 645 646 647 |
switch (sh_mobile_format_fourcc(&ch->info->var)) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: |
9a217e344 fbdev: sh_mobile_... |
648 |
pixelformat = SH_MOBILE_MERAM_PF_NV; |
edd153a3e fbdev: sh_mobile_... |
649 650 651 652 653 654 655 656 657 658 659 660 |
break; case V4L2_PIX_FMT_NV24: case V4L2_PIX_FMT_NV42: pixelformat = SH_MOBILE_MERAM_PF_NV24; break; case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_BGR32: default: pixelformat = SH_MOBILE_MERAM_PF_RGB; break; } |
9a217e344 fbdev: sh_mobile_... |
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 |
ret = mdev->ops->meram_register(mdev, cfg, ch->pitch, ch->info->var.yres, pixelformat, ch->base_addr_y, ch->base_addr_c, &ch->base_addr_y, &ch->base_addr_c, &ch->pitch); if (!ret) ch->meram_enabled = 1; } /* Start the LCDC. */ __sh_mobile_lcdc_start(priv); /* Setup deferred I/O, tell the board code to enable the panels, and * turn backlight on. */ |
cfb4f5d17 fbdev: SuperH Mob... |
677 678 |
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; |
21bc1f024 sh: skip disabled... |
679 680 |
if (!ch->enabled) continue; |
9a217e344 fbdev: sh_mobile_... |
681 682 683 684 685 686 687 |
tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; if (ch->ldmt1r_value & LDMT1R_IFM && tmp) { ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.delay = msecs_to_jiffies(tmp); ch->info->fbdefio = &ch->defio; fb_deferred_io_init(ch->info); } |
cfb4f5d17 fbdev: SuperH Mob... |
688 |
board_cfg = &ch->cfg.board_cfg; |
247f99386 fbdev: sh_mobile_... |
689 |
if (board_cfg->display_on && try_module_get(board_cfg->owner)) { |
c24393981 sh: add a paramet... |
690 |
board_cfg->display_on(board_cfg->board_data, ch->info); |
6de9edd5b fbdev: sh_mobile_... |
691 692 |
module_put(board_cfg->owner); } |
3b0fd9d75 fbdev: sh_mobile_... |
693 694 695 696 697 |
if (ch->bl) { ch->bl->props.power = FB_BLANK_UNBLANK; backlight_update_status(ch->bl); } |
cfb4f5d17 fbdev: SuperH Mob... |
698 699 700 701 702 703 704 705 706 707 |
} return 0; } static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_lcdc_chan *ch; struct sh_mobile_lcdc_board_cfg *board_cfg; int k; |
2feb075a3 video: sh_mobile_... |
708 |
/* clean up deferred io and ask board code to disable panel */ |
cfb4f5d17 fbdev: SuperH Mob... |
709 710 |
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; |
21bc1f024 sh: skip disabled... |
711 712 |
if (!ch->enabled) continue; |
8564557a0 video: sh_mobile_... |
713 |
|
2feb075a3 video: sh_mobile_... |
714 715 716 717 |
/* deferred io mode: * flush frame, and wait for frame end interrupt * clean up deferred io and enable clock */ |
5ef6b505d fbdev: sh_mobile_... |
718 |
if (ch->info && ch->info->fbdefio) { |
2feb075a3 video: sh_mobile_... |
719 |
ch->frame_end = 0; |
e33afddca video: sh_mobile_... |
720 |
schedule_delayed_work(&ch->info->deferred_work, 0); |
2feb075a3 video: sh_mobile_... |
721 |
wait_event(ch->frame_end_wait, ch->frame_end); |
e33afddca video: sh_mobile_... |
722 723 |
fb_deferred_io_cleanup(ch->info); ch->info->fbdefio = NULL; |
2feb075a3 video: sh_mobile_... |
724 |
sh_mobile_lcdc_clk_on(priv); |
8564557a0 video: sh_mobile_... |
725 |
} |
2feb075a3 video: sh_mobile_... |
726 |
|
3b0fd9d75 fbdev: sh_mobile_... |
727 728 729 730 |
if (ch->bl) { ch->bl->props.power = FB_BLANK_POWERDOWN; backlight_update_status(ch->bl); } |
2feb075a3 video: sh_mobile_... |
731 |
board_cfg = &ch->cfg.board_cfg; |
247f99386 fbdev: sh_mobile_... |
732 |
if (board_cfg->display_off && try_module_get(board_cfg->owner)) { |
2feb075a3 video: sh_mobile_... |
733 |
board_cfg->display_off(board_cfg->board_data); |
6de9edd5b fbdev: sh_mobile_... |
734 735 |
module_put(board_cfg->owner); } |
7caa4342c sh_mobile_meram: ... |
736 737 738 739 740 741 742 743 744 745 |
/* disable the meram */ if (ch->meram_enabled) { struct sh_mobile_meram_cfg *cfg; struct sh_mobile_meram_info *mdev; cfg = ch->cfg.meram_cfg; mdev = priv->meram_dev; mdev->ops->meram_unregister(mdev, cfg); ch->meram_enabled = 0; } |
cfb4f5d17 fbdev: SuperH Mob... |
746 747 748 |
} /* stop the lcdc */ |
8e9bb19ef video: stop sh_mo... |
749 750 751 752 |
if (priv->started) { sh_mobile_lcdc_start_stop(priv, 0); priv->started = 0; } |
b51339fff sh: sh_mobile lcd... |
753 |
|
8564557a0 video: sh_mobile_... |
754 755 756 757 |
/* stop clocks */ for (k = 0; k < ARRAY_SIZE(priv->ch); k++) if (priv->ch[k].enabled) sh_mobile_lcdc_clk_off(priv); |
cfb4f5d17 fbdev: SuperH Mob... |
758 759 760 761 |
} static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) { |
ce1c0b087 fbdev: sh_mobile_... |
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
int interface_type = ch->cfg.interface_type; switch (interface_type) { case RGB8: case RGB9: case RGB12A: case RGB12B: case RGB16: case RGB18: case RGB24: case SYS8A: case SYS8B: case SYS8C: case SYS8D: case SYS9: case SYS12: case SYS16A: case SYS16B: case SYS16C: case SYS18: case SYS24: break; default: return -EINVAL; |
cfb4f5d17 fbdev: SuperH Mob... |
786 787 788 789 |
} /* SUBLCD only supports SYS interface */ if (lcdc_chan_is_sublcd(ch)) { |
ce1c0b087 fbdev: sh_mobile_... |
790 791 792 793 |
if (!(interface_type & LDMT1R_IFM)) return -EINVAL; interface_type &= ~LDMT1R_IFM; |
cfb4f5d17 fbdev: SuperH Mob... |
794 |
} |
ce1c0b087 fbdev: sh_mobile_... |
795 |
ch->ldmt1r_value = interface_type; |
cfb4f5d17 fbdev: SuperH Mob... |
796 |
return 0; |
cfb4f5d17 fbdev: SuperH Mob... |
797 |
} |
b51339fff sh: sh_mobile lcd... |
798 799 |
static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, int clock_source, |
cfb4f5d17 fbdev: SuperH Mob... |
800 801 802 |
struct sh_mobile_lcdc_priv *priv) { char *str; |
cfb4f5d17 fbdev: SuperH Mob... |
803 804 |
switch (clock_source) { |
ce1c0b087 fbdev: sh_mobile_... |
805 806 807 808 809 810 811 812 813 814 815 816 |
case LCDC_CLK_BUS: str = "bus_clk"; priv->lddckr = LDDCKR_ICKSEL_BUS; break; case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; priv->lddckr = LDDCKR_ICKSEL_MIPI; break; case LCDC_CLK_EXTERNAL: str = NULL; priv->lddckr = LDDCKR_ICKSEL_HDMI; break; |
cfb4f5d17 fbdev: SuperH Mob... |
817 818 819 |
default: return -EINVAL; } |
cfb4f5d17 fbdev: SuperH Mob... |
820 |
if (str) { |
b51339fff sh: sh_mobile lcd... |
821 822 823 824 |
priv->dot_clk = clk_get(&pdev->dev, str); if (IS_ERR(priv->dot_clk)) { dev_err(&pdev->dev, "cannot get dot clock %s ", str); |
b51339fff sh: sh_mobile lcd... |
825 |
return PTR_ERR(priv->dot_clk); |
cfb4f5d17 fbdev: SuperH Mob... |
826 |
} |
cfb4f5d17 fbdev: SuperH Mob... |
827 |
} |
0246c4712 video: Runtime PM... |
828 829 830 831 832 |
/* Runtime PM support involves two step for this driver: * 1) Enable Runtime PM * 2) Force Runtime PM Resume since hardware is accessed from probe() */ |
8bed90557 sh: fix a number ... |
833 |
priv->dev = &pdev->dev; |
0246c4712 video: Runtime PM... |
834 835 |
pm_runtime_enable(priv->dev); pm_runtime_resume(priv->dev); |
cfb4f5d17 fbdev: SuperH Mob... |
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 |
return 0; } static int sh_mobile_lcdc_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info) { u32 *palette = info->pseudo_palette; if (regno >= PALETTE_NR) return -EINVAL; /* only FB_VISUAL_TRUECOLOR supported */ red >>= 16 - info->var.red.length; green >>= 16 - info->var.green.length; blue >>= 16 - info->var.blue.length; transp >>= 16 - info->var.transp.length; palette[regno] = (red << info->var.red.offset) | (green << info->var.green.offset) | (blue << info->var.blue.offset) | (transp << info->var.transp.offset); return 0; } static struct fb_fix_screeninfo sh_mobile_lcdc_fix = { .id = "SH Mobile LCDC", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_TRUECOLOR, .accel = FB_ACCEL_NONE, |
9dd38819c video: sh_mobile_... |
868 869 870 |
.xpanstep = 0, .ypanstep = 1, .ywrapstep = 0, |
edd153a3e fbdev: sh_mobile_... |
871 |
.capabilities = FB_CAP_FOURCC, |
cfb4f5d17 fbdev: SuperH Mob... |
872 |
}; |
8564557a0 video: sh_mobile_... |
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 |
static void sh_mobile_lcdc_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { sys_fillrect(info, rect); sh_mobile_lcdc_deferred_io_touch(info); } static void sh_mobile_lcdc_copyarea(struct fb_info *info, const struct fb_copyarea *area) { sys_copyarea(info, area); sh_mobile_lcdc_deferred_io_touch(info); } static void sh_mobile_lcdc_imageblit(struct fb_info *info, const struct fb_image *image) { sys_imageblit(info, image); sh_mobile_lcdc_deferred_io_touch(info); } |
9dd38819c video: sh_mobile_... |
893 894 895 896 |
static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; |
92e1f9a7e video: sh_mobile_... |
897 898 899 |
struct sh_mobile_lcdc_priv *priv = ch->lcdc; unsigned long ldrcntr; unsigned long new_pan_offset; |
53b503143 fbdev: sh_mobile_... |
900 901 |
unsigned long base_addr_y, base_addr_c; unsigned long c_offset; |
edd153a3e fbdev: sh_mobile_... |
902 |
bool yuv = sh_mobile_format_is_yuv(&info->var); |
92e1f9a7e video: sh_mobile_... |
903 |
|
edd153a3e fbdev: sh_mobile_... |
904 |
if (!yuv) |
dc1d5adab fbdev: sh_mobile_... |
905 906 |
new_pan_offset = var->yoffset * info->fix.line_length + var->xoffset * (info->var.bits_per_pixel / 8); |
53b503143 fbdev: sh_mobile_... |
907 |
else |
dc1d5adab fbdev: sh_mobile_... |
908 909 |
new_pan_offset = var->yoffset * info->fix.line_length + var->xoffset; |
9dd38819c video: sh_mobile_... |
910 |
|
92e1f9a7e video: sh_mobile_... |
911 |
if (new_pan_offset == ch->pan_offset) |
9dd38819c video: sh_mobile_... |
912 |
return 0; /* No change, do nothing */ |
92e1f9a7e video: sh_mobile_... |
913 |
ldrcntr = lcdc_read(priv, _LDRCNTR); |
9dd38819c video: sh_mobile_... |
914 |
|
92e1f9a7e video: sh_mobile_... |
915 |
/* Set the source address for the next refresh */ |
53b503143 fbdev: sh_mobile_... |
916 |
base_addr_y = ch->dma_handle + new_pan_offset; |
edd153a3e fbdev: sh_mobile_... |
917 |
if (yuv) { |
53b503143 fbdev: sh_mobile_... |
918 |
/* Set y offset */ |
dc1d5adab fbdev: sh_mobile_... |
919 920 921 922 923 |
c_offset = var->yoffset * info->fix.line_length * (info->var.bits_per_pixel - 8) / 8; base_addr_c = ch->dma_handle + info->var.xres * info->var.yres_virtual + c_offset; |
53b503143 fbdev: sh_mobile_... |
924 |
/* Set x offset */ |
edd153a3e fbdev: sh_mobile_... |
925 |
if (sh_mobile_format_fourcc(&info->var) == V4L2_PIX_FMT_NV24) |
53b503143 fbdev: sh_mobile_... |
926 927 928 |
base_addr_c += 2 * var->xoffset; else base_addr_c += var->xoffset; |
49d79ba2e fbdev: sh_mobile_... |
929 |
} |
53b503143 fbdev: sh_mobile_... |
930 |
|
49d79ba2e fbdev: sh_mobile_... |
931 |
if (ch->meram_enabled) { |
7caa4342c sh_mobile_meram: ... |
932 933 |
struct sh_mobile_meram_cfg *cfg; struct sh_mobile_meram_info *mdev; |
7caa4342c sh_mobile_meram: ... |
934 935 936 937 938 939 |
int ret; cfg = ch->cfg.meram_cfg; mdev = priv->meram_dev; ret = mdev->ops->meram_update(mdev, cfg, base_addr_y, base_addr_c, |
49d79ba2e fbdev: sh_mobile_... |
940 |
&base_addr_y, &base_addr_c); |
7caa4342c sh_mobile_meram: ... |
941 942 |
if (ret) return ret; |
49d79ba2e fbdev: sh_mobile_... |
943 |
} |
7caa4342c sh_mobile_meram: ... |
944 |
|
49d79ba2e fbdev: sh_mobile_... |
945 946 |
ch->base_addr_y = base_addr_y; ch->base_addr_c = base_addr_c; |
7caa4342c sh_mobile_meram: ... |
947 |
|
49d79ba2e fbdev: sh_mobile_... |
948 |
lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y); |
edd153a3e fbdev: sh_mobile_... |
949 |
if (yuv) |
49d79ba2e fbdev: sh_mobile_... |
950 |
lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c); |
53b503143 fbdev: sh_mobile_... |
951 |
|
92e1f9a7e video: sh_mobile_... |
952 953 954 955 956 957 958 959 |
if (lcdc_chan_is_sublcd(ch)) lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); else lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS); ch->pan_offset = new_pan_offset; sh_mobile_lcdc_deferred_io_touch(info); |
9dd38819c video: sh_mobile_... |
960 961 962 |
return 0; } |
40331b21f video: sh_mobile_... |
963 964 965 966 967 |
static int sh_mobile_wait_for_vsync(struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; unsigned long ldintr; int ret; |
dc48665fa fbdev: sh_mobile_... |
968 969 970 |
/* Enable VSync End interrupt and be careful not to acknowledge any * pending interrupt. */ |
40331b21f video: sh_mobile_... |
971 |
ldintr = lcdc_read(ch->lcdc, _LDINTR); |
dc48665fa fbdev: sh_mobile_... |
972 |
ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK; |
40331b21f video: sh_mobile_... |
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 |
lcdc_write(ch->lcdc, _LDINTR, ldintr); ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, msecs_to_jiffies(100)); if (!ret) return -ETIMEDOUT; return 0; } static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) { int retval; switch (cmd) { case FBIO_WAITFORVSYNC: retval = sh_mobile_wait_for_vsync(info); break; default: retval = -ENOIOCTLCMD; break; } return retval; } |
dd210503b fbdev: sh_mobile_... |
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 |
static void sh_mobile_fb_reconfig(struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; struct fb_videomode mode1, mode2; struct fb_event event; int evnt = FB_EVENT_MODE_CHANGE_ALL; if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par)) /* More framebuffer users are active */ return; fb_var_to_videomode(&mode1, &ch->display_var); fb_var_to_videomode(&mode2, &info->var); if (fb_mode_is_equal(&mode1, &mode2)) return; /* Display has been re-plugged, framebuffer is free now, reconfigure */ if (fb_set_var(info, &ch->display_var) < 0) /* Couldn't reconfigure, hopefully, can continue as before */ return; |
dd210503b fbdev: sh_mobile_... |
1020 1021 1022 1023 1024 1025 |
/* * fb_set_var() calls the notifier change internally, only if * FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a * user event, we have to call the chain ourselves. */ event.info = info; |
cc267ec5d fbdev: sh_mobile_... |
1026 |
event.data = &mode1; |
dd210503b fbdev: sh_mobile_... |
1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 |
fb_notifier_call_chain(evnt, &event); } /* * Locking: both .fb_release() and .fb_open() are called with info->lock held if * user == 1, or with console sem held, if user == 0. */ static int sh_mobile_release(struct fb_info *info, int user) { struct sh_mobile_lcdc_chan *ch = info->par; mutex_lock(&ch->open_lock); dev_dbg(info->dev, "%s(): %d users ", __func__, ch->use_count); ch->use_count--; /* Nothing to reconfigure, when called from fbcon */ if (user) { |
ac751efa6 console: rename a... |
1046 |
console_lock(); |
dd210503b fbdev: sh_mobile_... |
1047 |
sh_mobile_fb_reconfig(info); |
ac751efa6 console: rename a... |
1048 |
console_unlock(); |
dd210503b fbdev: sh_mobile_... |
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 |
} mutex_unlock(&ch->open_lock); return 0; } static int sh_mobile_open(struct fb_info *info, int user) { struct sh_mobile_lcdc_chan *ch = info->par; mutex_lock(&ch->open_lock); ch->use_count++; dev_dbg(info->dev, "%s(): %d users ", __func__, ch->use_count); mutex_unlock(&ch->open_lock); return 0; } static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; |
417d48274 fbdev: sh_mobile_... |
1073 |
struct sh_mobile_lcdc_priv *p = ch->lcdc; |
038621944 fbdev: sh_mobile_... |
1074 1075 1076 1077 |
unsigned int best_dist = (unsigned int)-1; unsigned int best_xres = 0; unsigned int best_yres = 0; unsigned int i; |
dd210503b fbdev: sh_mobile_... |
1078 |
|
038621944 fbdev: sh_mobile_... |
1079 |
if (var->xres > MAX_XRES || var->yres > MAX_YRES) |
dd210503b fbdev: sh_mobile_... |
1080 |
return -EINVAL; |
038621944 fbdev: sh_mobile_... |
1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 |
/* If board code provides us with a list of available modes, make sure * we use one of them. Find the mode closest to the requested one. The * distance between two modes is defined as the size of the * non-overlapping parts of the two rectangles. */ for (i = 0; i < ch->cfg.num_cfg; ++i) { const struct fb_videomode *mode = &ch->cfg.lcd_cfg[i]; unsigned int dist; /* We can only round up. */ if (var->xres > mode->xres || var->yres > mode->yres) continue; dist = var->xres * var->yres + mode->xres * mode->yres - 2 * min(var->xres, mode->xres) * min(var->yres, mode->yres); if (dist < best_dist) { best_xres = mode->xres; best_yres = mode->yres; best_dist = dist; } |
dd210503b fbdev: sh_mobile_... |
1104 |
} |
417d48274 fbdev: sh_mobile_... |
1105 |
|
038621944 fbdev: sh_mobile_... |
1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 |
/* If no available mode can be used, return an error. */ if (ch->cfg.num_cfg != 0) { if (best_dist == (unsigned int)-1) return -EINVAL; var->xres = best_xres; var->yres = best_yres; } /* Make sure the virtual resolution is at least as big as the visible * resolution. */ if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) var->yres_virtual = var->yres; |
edd153a3e fbdev: sh_mobile_... |
1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 |
if (sh_mobile_format_is_fourcc(var)) { switch (var->grayscale) { case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: var->bits_per_pixel = 12; break; case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_NV16: case V4L2_PIX_FMT_NV61: var->bits_per_pixel = 16; break; case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_NV24: case V4L2_PIX_FMT_NV42: var->bits_per_pixel = 24; break; case V4L2_PIX_FMT_BGR32: var->bits_per_pixel = 32; break; default: return -EINVAL; } /* Default to RGB and JPEG color-spaces for RGB and YUV formats * respectively. */ if (!sh_mobile_format_is_yuv(var)) var->colorspace = V4L2_COLORSPACE_SRGB; else if (var->colorspace != V4L2_COLORSPACE_REC709) var->colorspace = V4L2_COLORSPACE_JPEG; } else { if (var->bits_per_pixel <= 16) { /* RGB 565 */ var->bits_per_pixel = 16; var->red.offset = 11; var->red.length = 5; var->green.offset = 5; var->green.length = 6; var->blue.offset = 0; var->blue.length = 5; var->transp.offset = 0; var->transp.length = 0; } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ var->bits_per_pixel = 24; var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 0; var->transp.length = 0; } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ var->bits_per_pixel = 32; var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; var->transp.offset = 24; var->transp.length = 8; } else return -EINVAL; |
417d48274 fbdev: sh_mobile_... |
1185 |
|
edd153a3e fbdev: sh_mobile_... |
1186 1187 1188 1189 1190 |
var->red.msb_right = 0; var->green.msb_right = 0; var->blue.msb_right = 0; var->transp.msb_right = 0; } |
038621944 fbdev: sh_mobile_... |
1191 1192 1193 1194 1195 |
/* Make sure we don't exceed our allocated memory. */ if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > info->fix.smem_len) return -EINVAL; |
edd153a3e fbdev: sh_mobile_... |
1196 1197 1198 |
/* only accept the forced_fourcc for dual channel configurations */ if (p->forced_fourcc && p->forced_fourcc != sh_mobile_format_fourcc(var)) |
417d48274 fbdev: sh_mobile_... |
1199 |
return -EINVAL; |
417d48274 fbdev: sh_mobile_... |
1200 |
|
dd210503b fbdev: sh_mobile_... |
1201 1202 |
return 0; } |
40331b21f video: sh_mobile_... |
1203 |
|
ed5bebf29 fbdev: sh_mobile_... |
1204 1205 1206 |
static int sh_mobile_set_par(struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; |
91fba48d5 fbdev: sh_mobile_... |
1207 |
u32 line_length = info->fix.line_length; |
ed5bebf29 fbdev: sh_mobile_... |
1208 1209 1210 |
int ret; sh_mobile_lcdc_stop(ch->lcdc); |
91fba48d5 fbdev: sh_mobile_... |
1211 |
|
edd153a3e fbdev: sh_mobile_... |
1212 |
if (sh_mobile_format_is_yuv(&info->var)) |
91fba48d5 fbdev: sh_mobile_... |
1213 1214 1215 1216 |
info->fix.line_length = info->var.xres; else info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8; |
ed5bebf29 fbdev: sh_mobile_... |
1217 |
ret = sh_mobile_lcdc_start(ch->lcdc); |
91fba48d5 fbdev: sh_mobile_... |
1218 |
if (ret < 0) { |
ed5bebf29 fbdev: sh_mobile_... |
1219 1220 |
dev_err(info->dev, "%s: unable to restart LCDC ", __func__); |
91fba48d5 fbdev: sh_mobile_... |
1221 1222 |
info->fix.line_length = line_length; } |
ed5bebf29 fbdev: sh_mobile_... |
1223 |
|
edd153a3e fbdev: sh_mobile_... |
1224 1225 1226 1227 1228 1229 1230 |
if (sh_mobile_format_is_fourcc(&info->var)) { info->fix.type = FB_TYPE_FOURCC; info->fix.visual = FB_VISUAL_FOURCC; } else { info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.visual = FB_VISUAL_TRUECOLOR; } |
ed5bebf29 fbdev: sh_mobile_... |
1231 1232 |
return ret; } |
8857b9aa7 fbdev: sh_mobile_... |
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 |
/* * Screen blanking. Behavior is as follows: * FB_BLANK_UNBLANK: screen unblanked, clocks enabled * FB_BLANK_NORMAL: screen blanked, clocks enabled * FB_BLANK_VSYNC, * FB_BLANK_HSYNC, * FB_BLANK_POWEROFF: screen blanked, clocks disabled */ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info) { struct sh_mobile_lcdc_chan *ch = info->par; struct sh_mobile_lcdc_priv *p = ch->lcdc; /* blank the screen? */ if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) { struct fb_fillrect rect = { .width = info->var.xres, .height = info->var.yres, }; sh_mobile_lcdc_fillrect(info, &rect); } /* turn clocks on? */ if (blank <= FB_BLANK_NORMAL && ch->blank_status > FB_BLANK_NORMAL) { sh_mobile_lcdc_clk_on(p); } /* turn clocks off? */ if (blank > FB_BLANK_NORMAL && ch->blank_status <= FB_BLANK_NORMAL) { /* make sure the screen is updated with the black fill before * switching the clocks off. one vsync is not enough since * blanking may occur in the middle of a refresh. deferred io * mode will reenable the clocks and update the screen in time, * so it does not need this. */ if (!info->fbdefio) { sh_mobile_wait_for_vsync(info); sh_mobile_wait_for_vsync(info); } sh_mobile_lcdc_clk_off(p); } ch->blank_status = blank; return 0; } |
cfb4f5d17 fbdev: SuperH Mob... |
1275 |
static struct fb_ops sh_mobile_lcdc_ops = { |
9dd38819c video: sh_mobile_... |
1276 |
.owner = THIS_MODULE, |
cfb4f5d17 fbdev: SuperH Mob... |
1277 |
.fb_setcolreg = sh_mobile_lcdc_setcolreg, |
2540c111e sh_mobile_lcdc: u... |
1278 1279 |
.fb_read = fb_sys_read, .fb_write = fb_sys_write, |
8564557a0 video: sh_mobile_... |
1280 1281 1282 |
.fb_fillrect = sh_mobile_lcdc_fillrect, .fb_copyarea = sh_mobile_lcdc_copyarea, .fb_imageblit = sh_mobile_lcdc_imageblit, |
8857b9aa7 fbdev: sh_mobile_... |
1283 |
.fb_blank = sh_mobile_lcdc_blank, |
9dd38819c video: sh_mobile_... |
1284 |
.fb_pan_display = sh_mobile_fb_pan_display, |
40331b21f video: sh_mobile_... |
1285 |
.fb_ioctl = sh_mobile_ioctl, |
dd210503b fbdev: sh_mobile_... |
1286 1287 1288 |
.fb_open = sh_mobile_open, .fb_release = sh_mobile_release, .fb_check_var = sh_mobile_check_var, |
ed5bebf29 fbdev: sh_mobile_... |
1289 |
.fb_set_par = sh_mobile_set_par, |
cfb4f5d17 fbdev: SuperH Mob... |
1290 |
}; |
3b0fd9d75 fbdev: sh_mobile_... |
1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 |
static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev) { struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg; int brightness = bdev->props.brightness; if (bdev->props.power != FB_BLANK_UNBLANK || bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) brightness = 0; return cfg->set_brightness(cfg->board_data, brightness); } static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev) { struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev); struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg; return cfg->get_brightness(cfg->board_data); } static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev, struct fb_info *info) { return (info->bl_dev == bdev); } static struct backlight_ops sh_mobile_lcdc_bl_ops = { .options = BL_CORE_SUSPENDRESUME, .update_status = sh_mobile_lcdc_update_bl, .get_brightness = sh_mobile_lcdc_get_brightness, .check_fb = sh_mobile_lcdc_check_fb, }; static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent, struct sh_mobile_lcdc_chan *ch) { struct backlight_device *bl; bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch, &sh_mobile_lcdc_bl_ops, NULL); |
beee1f20a fbdev: sh_mobile_... |
1332 1333 1334 1335 |
if (IS_ERR(bl)) { dev_err(parent, "unable to register backlight device: %ld ", PTR_ERR(bl)); |
3b0fd9d75 fbdev: sh_mobile_... |
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 |
return NULL; } bl->props.max_brightness = ch->cfg.bl_info.max_brightness; bl->props.brightness = bl->props.max_brightness; backlight_update_status(bl); return bl; } static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev) { backlight_device_unregister(bdev); } |
2feb075a3 video: sh_mobile_... |
1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 |
static int sh_mobile_lcdc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); sh_mobile_lcdc_stop(platform_get_drvdata(pdev)); return 0; } static int sh_mobile_lcdc_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); return sh_mobile_lcdc_start(platform_get_drvdata(pdev)); } |
0246c4712 video: Runtime PM... |
1364 1365 1366 |
static int sh_mobile_lcdc_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); |
2427bb241 fbdev: sh_mobile_... |
1367 |
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); |
0246c4712 video: Runtime PM... |
1368 1369 |
/* turn off LCDC hardware */ |
2427bb241 fbdev: sh_mobile_... |
1370 |
lcdc_write(priv, _LDCNT1R, 0); |
0246c4712 video: Runtime PM... |
1371 1372 1373 1374 1375 1376 |
return 0; } static int sh_mobile_lcdc_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); |
2427bb241 fbdev: sh_mobile_... |
1377 |
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); |
0246c4712 video: Runtime PM... |
1378 |
|
2427bb241 fbdev: sh_mobile_... |
1379 |
__sh_mobile_lcdc_start(priv); |
0246c4712 video: Runtime PM... |
1380 1381 1382 |
return 0; } |
471452104 const: constify r... |
1383 |
static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { |
2feb075a3 video: sh_mobile_... |
1384 1385 |
.suspend = sh_mobile_lcdc_suspend, .resume = sh_mobile_lcdc_resume, |
0246c4712 video: Runtime PM... |
1386 1387 |
.runtime_suspend = sh_mobile_lcdc_runtime_suspend, .runtime_resume = sh_mobile_lcdc_runtime_resume, |
2feb075a3 video: sh_mobile_... |
1388 |
}; |
6de9edd5b fbdev: sh_mobile_... |
1389 |
/* locking: called with info->lock held */ |
6011bdeaa fbdev: sh-mobile:... |
1390 1391 1392 1393 1394 1395 1396 |
static int sh_mobile_lcdc_notify(struct notifier_block *nb, unsigned long action, void *data) { struct fb_event *event = data; struct fb_info *info = event->info; struct sh_mobile_lcdc_chan *ch = info->par; struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; |
6011bdeaa fbdev: sh-mobile:... |
1397 1398 |
if (&ch->lcdc->notifier != nb) |
baf163749 fbdev: sh_mobile_... |
1399 |
return NOTIFY_DONE; |
6011bdeaa fbdev: sh-mobile:... |
1400 1401 1402 1403 1404 1405 1406 |
dev_dbg(info->dev, "%s(): action = %lu, data = %p ", __func__, action, event->data); switch(action) { case FB_EVENT_SUSPEND: |
247f99386 fbdev: sh_mobile_... |
1407 |
if (board_cfg->display_off && try_module_get(board_cfg->owner)) { |
6011bdeaa fbdev: sh-mobile:... |
1408 |
board_cfg->display_off(board_cfg->board_data); |
6de9edd5b fbdev: sh_mobile_... |
1409 1410 |
module_put(board_cfg->owner); } |
afe417c03 fbdev: sh_mobile_... |
1411 |
sh_mobile_lcdc_stop(ch->lcdc); |
6011bdeaa fbdev: sh-mobile:... |
1412 1413 |
break; case FB_EVENT_RESUME: |
dd210503b fbdev: sh_mobile_... |
1414 1415 1416 |
mutex_lock(&ch->open_lock); sh_mobile_fb_reconfig(info); mutex_unlock(&ch->open_lock); |
6011bdeaa fbdev: sh-mobile:... |
1417 1418 |
/* HDMI must be enabled before LCDC configuration */ |
247f99386 fbdev: sh_mobile_... |
1419 |
if (board_cfg->display_on && try_module_get(board_cfg->owner)) { |
dd210503b fbdev: sh_mobile_... |
1420 |
board_cfg->display_on(board_cfg->board_data, info); |
6de9edd5b fbdev: sh_mobile_... |
1421 |
module_put(board_cfg->owner); |
6011bdeaa fbdev: sh-mobile:... |
1422 |
} |
ebe5e12d0 fbdev: sh_mobile_... |
1423 |
sh_mobile_lcdc_start(ch->lcdc); |
6011bdeaa fbdev: sh-mobile:... |
1424 |
} |
baf163749 fbdev: sh_mobile_... |
1425 |
return NOTIFY_OK; |
6011bdeaa fbdev: sh-mobile:... |
1426 |
} |
b4bee692e fbdev: sh_mobile_... |
1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 |
static int sh_mobile_lcdc_remove(struct platform_device *pdev) { struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); struct fb_info *info; int i; fb_unregister_client(&priv->notifier); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) if (priv->ch[i].info && priv->ch[i].info->dev) unregister_framebuffer(priv->ch[i].info); sh_mobile_lcdc_stop(priv); for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { info = priv->ch[i].info; if (!info || !info->device) continue; if (priv->ch[i].sglist) vfree(priv->ch[i].sglist); if (info->screen_base) dma_free_coherent(&pdev->dev, info->fix.smem_len, info->screen_base, priv->ch[i].dma_handle); fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { if (priv->ch[i].bl) sh_mobile_lcdc_bl_remove(priv->ch[i].bl); } if (priv->dot_clk) clk_put(priv->dot_clk); if (priv->dev) pm_runtime_disable(priv->dev); if (priv->base) iounmap(priv->base); if (priv->irq) free_irq(priv->irq, priv); kfree(priv); return 0; } |
cfb4f5d17 fbdev: SuperH Mob... |
1477 |
|
3ce055999 fbdev: sh_mobile_... |
1478 1479 |
static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch, struct device *dev) |
cfb4f5d17 fbdev: SuperH Mob... |
1480 |
{ |
3ce055999 fbdev: sh_mobile_... |
1481 1482 1483 1484 |
struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg; const struct fb_videomode *max_mode; const struct fb_videomode *mode; struct fb_var_screeninfo *var; |
cfb4f5d17 fbdev: SuperH Mob... |
1485 |
struct fb_info *info; |
3ce055999 fbdev: sh_mobile_... |
1486 1487 1488 1489 1490 |
unsigned int max_size; int num_cfg; void *buf; int ret; int i; |
a67472ad1 fbdev: sh_mobile_... |
1491 1492 1493 |
mutex_init(&ch->open_lock); /* Allocate the frame buffer device. */ |
3ce055999 fbdev: sh_mobile_... |
1494 1495 1496 1497 1498 1499 1500 1501 |
ch->info = framebuffer_alloc(0, dev); if (!ch->info) { dev_err(dev, "unable to allocate fb_info "); return -ENOMEM; } info = ch->info; |
3ce055999 fbdev: sh_mobile_... |
1502 1503 |
info->fbops = &sh_mobile_lcdc_ops; info->par = ch; |
a67472ad1 fbdev: sh_mobile_... |
1504 1505 |
info->pseudo_palette = &ch->pseudo_palette; info->flags = FBINFO_FLAG_DEFAULT; |
3ce055999 fbdev: sh_mobile_... |
1506 1507 1508 1509 1510 1511 1512 1513 1514 |
/* Iterate through the modes to validate them and find the highest * resolution. */ max_mode = NULL; max_size = 0; for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) { unsigned int size = mode->yres * mode->xres; |
edd153a3e fbdev: sh_mobile_... |
1515 1516 1517 |
/* NV12/NV21 buffers must have even number of lines */ if ((cfg->fourcc == V4L2_PIX_FMT_NV12 || cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) { |
3ce055999 fbdev: sh_mobile_... |
1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 |
dev_err(dev, "yres must be multiple of 2 for YCbCr420 " "mode. "); return -EINVAL; } if (size > max_size) { max_mode = mode; max_size = size; } } if (!max_size) max_size = MAX_XRES * MAX_YRES; else dev_dbg(dev, "Found largest videomode %ux%u ", max_mode->xres, max_mode->yres); |
a67472ad1 fbdev: sh_mobile_... |
1536 |
/* Create the mode list. */ |
3ce055999 fbdev: sh_mobile_... |
1537 1538 1539 1540 1541 1542 1543 1544 1545 |
if (cfg->lcd_cfg == NULL) { mode = &default_720p; num_cfg = 1; } else { mode = cfg->lcd_cfg; num_cfg = cfg->num_cfg; } fb_videomode_to_modelist(mode, num_cfg, &info->modelist); |
a67472ad1 fbdev: sh_mobile_... |
1546 1547 1548 1549 1550 |
/* Initialize variable screen information using the first mode as * default. The default Y virtual resolution is twice the panel size to * allow for double-buffering. */ var = &info->var; |
3ce055999 fbdev: sh_mobile_... |
1551 1552 1553 |
fb_videomode_to_var(var, mode); var->width = cfg->lcd_size_cfg.width; var->height = cfg->lcd_size_cfg.height; |
3ce055999 fbdev: sh_mobile_... |
1554 1555 |
var->yres_virtual = var->yres * 2; var->activate = FB_ACTIVATE_NOW; |
edd153a3e fbdev: sh_mobile_... |
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 |
switch (cfg->fourcc) { case V4L2_PIX_FMT_RGB565: var->bits_per_pixel = 16; break; case V4L2_PIX_FMT_BGR24: var->bits_per_pixel = 24; break; case V4L2_PIX_FMT_BGR32: var->bits_per_pixel = 32; break; default: var->grayscale = cfg->fourcc; break; } /* Make sure the memory size check won't fail. smem_len is initialized * later based on var. */ info->fix.smem_len = UINT_MAX; |
a67472ad1 fbdev: sh_mobile_... |
1575 |
ret = sh_mobile_check_var(var, info); |
3ce055999 fbdev: sh_mobile_... |
1576 1577 |
if (ret) return ret; |
edd153a3e fbdev: sh_mobile_... |
1578 |
max_size = max_size * var->bits_per_pixel / 8 * 2; |
a67472ad1 fbdev: sh_mobile_... |
1579 |
/* Allocate frame buffer memory and color map. */ |
edd153a3e fbdev: sh_mobile_... |
1580 |
buf = dma_alloc_coherent(dev, max_size, &ch->dma_handle, GFP_KERNEL); |
3ce055999 fbdev: sh_mobile_... |
1581 1582 1583 1584 1585 |
if (!buf) { dev_err(dev, "unable to allocate buffer "); return -ENOMEM; } |
3ce055999 fbdev: sh_mobile_... |
1586 1587 1588 1589 |
ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); if (ret < 0) { dev_err(dev, "unable to allocate cmap "); |
edd153a3e fbdev: sh_mobile_... |
1590 |
dma_free_coherent(dev, max_size, buf, ch->dma_handle); |
3ce055999 fbdev: sh_mobile_... |
1591 1592 |
return ret; } |
edd153a3e fbdev: sh_mobile_... |
1593 1594 1595 1596 |
/* Initialize fixed screen information. Restrict pan to 2 lines steps * for NV12 and NV21. */ info->fix = sh_mobile_lcdc_fix; |
3ce055999 fbdev: sh_mobile_... |
1597 |
info->fix.smem_start = ch->dma_handle; |
edd153a3e fbdev: sh_mobile_... |
1598 1599 1600 1601 1602 1603 |
info->fix.smem_len = max_size; if (cfg->fourcc == V4L2_PIX_FMT_NV12 || cfg->fourcc == V4L2_PIX_FMT_NV21) info->fix.ypanstep = 2; if (sh_mobile_format_is_yuv(var)) { |
3ce055999 fbdev: sh_mobile_... |
1604 |
info->fix.line_length = var->xres; |
edd153a3e fbdev: sh_mobile_... |
1605 1606 1607 1608 1609 |
info->fix.visual = FB_VISUAL_FOURCC; } else { info->fix.line_length = var->xres * var->bits_per_pixel / 8; info->fix.visual = FB_VISUAL_TRUECOLOR; } |
3ce055999 fbdev: sh_mobile_... |
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 |
info->screen_base = buf; info->device = dev; ch->display_var = *var; return 0; } static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) { |
01ac25b59 fbdev: sh_mobile_... |
1620 |
struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data; |
3ce055999 fbdev: sh_mobile_... |
1621 |
struct sh_mobile_lcdc_priv *priv; |
cfb4f5d17 fbdev: SuperH Mob... |
1622 |
struct resource *res; |
3ce055999 fbdev: sh_mobile_... |
1623 |
int num_channels; |
cfb4f5d17 fbdev: SuperH Mob... |
1624 |
int error; |
3ce055999 fbdev: sh_mobile_... |
1625 |
int i; |
cfb4f5d17 fbdev: SuperH Mob... |
1626 |
|
01ac25b59 fbdev: sh_mobile_... |
1627 |
if (!pdata) { |
cfb4f5d17 fbdev: SuperH Mob... |
1628 1629 |
dev_err(&pdev->dev, "no platform data defined "); |
8bed90557 sh: fix a number ... |
1630 |
return -EINVAL; |
cfb4f5d17 fbdev: SuperH Mob... |
1631 1632 1633 |
} res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
8564557a0 video: sh_mobile_... |
1634 1635 1636 1637 |
i = platform_get_irq(pdev, 0); if (!res || i < 0) { dev_err(&pdev->dev, "cannot get platform resources "); |
8bed90557 sh: fix a number ... |
1638 |
return -ENOENT; |
cfb4f5d17 fbdev: SuperH Mob... |
1639 1640 1641 1642 1643 1644 |
} priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { dev_err(&pdev->dev, "cannot allocate device data "); |
8bed90557 sh: fix a number ... |
1645 |
return -ENOMEM; |
cfb4f5d17 fbdev: SuperH Mob... |
1646 |
} |
8bed90557 sh: fix a number ... |
1647 |
platform_set_drvdata(pdev, priv); |
f8798ccbe video: irq: Remov... |
1648 |
error = request_irq(i, sh_mobile_lcdc_irq, 0, |
7ad33e748 video: struct dev... |
1649 |
dev_name(&pdev->dev), priv); |
8564557a0 video: sh_mobile_... |
1650 1651 1652 1653 1654 1655 1656 |
if (error) { dev_err(&pdev->dev, "unable to request irq "); goto err1; } priv->irq = i; |
5ef6b505d fbdev: sh_mobile_... |
1657 |
atomic_set(&priv->hw_usecnt, -1); |
cfb4f5d17 fbdev: SuperH Mob... |
1658 |
|
3ce055999 fbdev: sh_mobile_... |
1659 1660 |
for (i = 0, num_channels = 0; i < ARRAY_SIZE(pdata->ch); i++) { struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels; |
cfb4f5d17 fbdev: SuperH Mob... |
1661 |
|
01ac25b59 fbdev: sh_mobile_... |
1662 1663 |
ch->lcdc = priv; memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i])); |
cfb4f5d17 fbdev: SuperH Mob... |
1664 |
|
01ac25b59 fbdev: sh_mobile_... |
1665 |
error = sh_mobile_lcdc_check_interface(ch); |
cfb4f5d17 fbdev: SuperH Mob... |
1666 1667 1668 1669 1670 |
if (error) { dev_err(&pdev->dev, "unsupported interface type "); goto err1; } |
01ac25b59 fbdev: sh_mobile_... |
1671 1672 1673 |
init_waitqueue_head(&ch->frame_end_wait); init_completion(&ch->vsync_completion); ch->pan_offset = 0; |
cfb4f5d17 fbdev: SuperH Mob... |
1674 |
|
3b0fd9d75 fbdev: sh_mobile_... |
1675 1676 1677 |
/* probe the backlight is there is one defined */ if (ch->cfg.bl_info.max_brightness) ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch); |
cfb4f5d17 fbdev: SuperH Mob... |
1678 1679 |
switch (pdata->ch[i].chan) { case LCDC_CHAN_MAINLCD: |
ce1c0b087 fbdev: sh_mobile_... |
1680 |
ch->enabled = LDCNT2R_ME; |
01ac25b59 fbdev: sh_mobile_... |
1681 |
ch->reg_offs = lcdc_offs_mainlcd; |
3ce055999 fbdev: sh_mobile_... |
1682 |
num_channels++; |
cfb4f5d17 fbdev: SuperH Mob... |
1683 1684 |
break; case LCDC_CHAN_SUBLCD: |
ce1c0b087 fbdev: sh_mobile_... |
1685 |
ch->enabled = LDCNT2R_SE; |
01ac25b59 fbdev: sh_mobile_... |
1686 |
ch->reg_offs = lcdc_offs_sublcd; |
3ce055999 fbdev: sh_mobile_... |
1687 |
num_channels++; |
cfb4f5d17 fbdev: SuperH Mob... |
1688 1689 1690 |
break; } } |
3ce055999 fbdev: sh_mobile_... |
1691 |
if (!num_channels) { |
cfb4f5d17 fbdev: SuperH Mob... |
1692 1693 1694 1695 1696 |
dev_err(&pdev->dev, "no channels defined "); error = -EINVAL; goto err1; } |
edd153a3e fbdev: sh_mobile_... |
1697 |
/* for dual channel LCDC (MAIN + SUB) force shared format setting */ |
3ce055999 fbdev: sh_mobile_... |
1698 |
if (num_channels == 2) |
edd153a3e fbdev: sh_mobile_... |
1699 |
priv->forced_fourcc = pdata->ch[0].fourcc; |
417d48274 fbdev: sh_mobile_... |
1700 |
|
dba6f385b fbdev: sh-mobile-... |
1701 1702 1703 |
priv->base = ioremap_nocache(res->start, resource_size(res)); if (!priv->base) goto err1; |
b51339fff sh: sh_mobile lcd... |
1704 |
error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); |
cfb4f5d17 fbdev: SuperH Mob... |
1705 1706 1707 1708 1709 |
if (error) { dev_err(&pdev->dev, "unable to setup clocks "); goto err1; } |
7caa4342c sh_mobile_meram: ... |
1710 |
priv->meram_dev = pdata->meram_dev; |
3ce055999 fbdev: sh_mobile_... |
1711 |
for (i = 0; i < num_channels; i++) { |
01ac25b59 fbdev: sh_mobile_... |
1712 |
struct sh_mobile_lcdc_chan *ch = priv->ch + i; |
c44f9f76d fbdev: sh_mobile_... |
1713 |
|
3ce055999 fbdev: sh_mobile_... |
1714 |
error = sh_mobile_lcdc_channel_init(ch, &pdev->dev); |
cfb4f5d17 fbdev: SuperH Mob... |
1715 |
if (error) |
3ce055999 fbdev: sh_mobile_... |
1716 |
goto err1; |
cfb4f5d17 fbdev: SuperH Mob... |
1717 |
} |
cfb4f5d17 fbdev: SuperH Mob... |
1718 1719 1720 1721 1722 1723 |
error = sh_mobile_lcdc_start(priv); if (error) { dev_err(&pdev->dev, "unable to start hardware "); goto err1; } |
3ce055999 fbdev: sh_mobile_... |
1724 |
for (i = 0; i < num_channels; i++) { |
1c6a307a5 sh: LCDC dcache f... |
1725 |
struct sh_mobile_lcdc_chan *ch = priv->ch + i; |
3ce055999 fbdev: sh_mobile_... |
1726 |
struct fb_info *info = ch->info; |
1c6a307a5 sh: LCDC dcache f... |
1727 1728 |
if (info->fbdefio) { |
8bed90557 sh: fix a number ... |
1729 |
ch->sglist = vmalloc(sizeof(struct scatterlist) * |
1c6a307a5 sh: LCDC dcache f... |
1730 |
info->fix.smem_len >> PAGE_SHIFT); |
8bed90557 sh: fix a number ... |
1731 |
if (!ch->sglist) { |
1c6a307a5 sh: LCDC dcache f... |
1732 1733 1734 1735 1736 |
dev_err(&pdev->dev, "cannot allocate sglist "); goto err1; } } |
3b0fd9d75 fbdev: sh_mobile_... |
1737 |
info->bl_dev = ch->bl; |
1c6a307a5 sh: LCDC dcache f... |
1738 |
error = register_framebuffer(info); |
cfb4f5d17 fbdev: SuperH Mob... |
1739 1740 |
if (error < 0) goto err1; |
cfb4f5d17 fbdev: SuperH Mob... |
1741 |
|
edd153a3e fbdev: sh_mobile_... |
1742 1743 1744 1745 1746 |
dev_info(info->dev, "registered %s/%s as %dx%d %dbpp. ", pdev->name, (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? "mainlcd" : "sublcd", info->var.xres, info->var.yres, info->var.bits_per_pixel); |
8564557a0 video: sh_mobile_... |
1747 1748 |
/* deferred io mode: disable clock to save power */ |
6011bdeaa fbdev: sh-mobile:... |
1749 |
if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) |
8564557a0 video: sh_mobile_... |
1750 |
sh_mobile_lcdc_clk_off(priv); |
cfb4f5d17 fbdev: SuperH Mob... |
1751 |
} |
6011bdeaa fbdev: sh-mobile:... |
1752 1753 1754 |
/* Failure ignored */ priv->notifier.notifier_call = sh_mobile_lcdc_notify; fb_register_client(&priv->notifier); |
cfb4f5d17 fbdev: SuperH Mob... |
1755 |
return 0; |
8bed90557 sh: fix a number ... |
1756 |
err1: |
cfb4f5d17 fbdev: SuperH Mob... |
1757 |
sh_mobile_lcdc_remove(pdev); |
8bed90557 sh: fix a number ... |
1758 |
|
cfb4f5d17 fbdev: SuperH Mob... |
1759 1760 |
return error; } |
cfb4f5d17 fbdev: SuperH Mob... |
1761 1762 1763 1764 |
static struct platform_driver sh_mobile_lcdc_driver = { .driver = { .name = "sh_mobile_lcdc_fb", .owner = THIS_MODULE, |
2feb075a3 video: sh_mobile_... |
1765 |
.pm = &sh_mobile_lcdc_dev_pm_ops, |
cfb4f5d17 fbdev: SuperH Mob... |
1766 1767 1768 1769 |
}, .probe = sh_mobile_lcdc_probe, .remove = sh_mobile_lcdc_remove, }; |
4277f2c46 video: convert dr... |
1770 |
module_platform_driver(sh_mobile_lcdc_driver); |
cfb4f5d17 fbdev: SuperH Mob... |
1771 1772 1773 1774 |
MODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver"); MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); MODULE_LICENSE("GPL v2"); |