Vitaly Wool | 36c9366 | 2006-07-03 00:24:19 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * drivers/video/pnx4008/sdum.c |
| 3 | * |
| 4 | * Display Update Master support |
| 5 | * |
| 6 | * Authors: Grigory Tolstolytkin <gtolstolytkin@ru.mvista.com> |
| 7 | * Vitaly Wool <vitalywool@gmail.com> |
| 8 | * Based on Philips Semiconductors's code |
| 9 | * |
| 10 | * Copyrght (c) 2005-2006 MontaVista Software, Inc. |
| 11 | * Copyright (c) 2005 Philips Semiconductors |
| 12 | * This file is licensed under the terms of the GNU General Public License |
| 13 | * version 2. This program is licensed "as is" without any warranty of any |
| 14 | * kind, whether express or implied. |
| 15 | */ |
| 16 | |
| 17 | #include <linux/module.h> |
| 18 | #include <linux/kernel.h> |
| 19 | #include <linux/errno.h> |
| 20 | #include <linux/string.h> |
| 21 | #include <linux/mm.h> |
| 22 | #include <linux/tty.h> |
| 23 | #include <linux/slab.h> |
| 24 | #include <linux/vmalloc.h> |
| 25 | #include <linux/delay.h> |
| 26 | #include <linux/interrupt.h> |
| 27 | #include <linux/platform_device.h> |
| 28 | #include <linux/fb.h> |
| 29 | #include <linux/init.h> |
| 30 | #include <linux/dma-mapping.h> |
| 31 | #include <linux/clk.h> |
| 32 | #include <asm/uaccess.h> |
| 33 | #include <asm/arch/gpio.h> |
| 34 | |
| 35 | #include "sdum.h" |
| 36 | #include "fbcommon.h" |
| 37 | #include "dum.h" |
| 38 | |
| 39 | /* Framebuffers we have */ |
| 40 | |
| 41 | static struct pnx4008_fb_addr { |
| 42 | int fb_type; |
| 43 | long addr_offset; |
| 44 | long fb_length; |
| 45 | } fb_addr[] = { |
| 46 | [0] = { |
| 47 | FB_TYPE_YUV, 0, 0xB0000 |
| 48 | }, |
| 49 | [1] = { |
| 50 | FB_TYPE_RGB, 0xB0000, 0x50000 |
| 51 | }, |
| 52 | }; |
| 53 | |
| 54 | static struct dum_data { |
| 55 | u32 lcd_phys_start; |
| 56 | u32 lcd_virt_start; |
| 57 | u32 slave_phys_base; |
| 58 | u32 *slave_virt_base; |
| 59 | int fb_owning_channel[MAX_DUM_CHANNELS]; |
| 60 | struct dumchannel_uf chan_uf_store[MAX_DUM_CHANNELS]; |
| 61 | } dum_data; |
| 62 | |
| 63 | /* Different local helper functions */ |
| 64 | |
| 65 | static u32 nof_pixels_dx(struct dum_ch_setup *ch_setup) |
| 66 | { |
| 67 | return (ch_setup->xmax - ch_setup->xmin + 1); |
| 68 | } |
| 69 | |
| 70 | static u32 nof_pixels_dy(struct dum_ch_setup *ch_setup) |
| 71 | { |
| 72 | return (ch_setup->ymax - ch_setup->ymin + 1); |
| 73 | } |
| 74 | |
| 75 | static u32 nof_pixels_dxy(struct dum_ch_setup *ch_setup) |
| 76 | { |
| 77 | return (nof_pixels_dx(ch_setup) * nof_pixels_dy(ch_setup)); |
| 78 | } |
| 79 | |
| 80 | static u32 nof_bytes(struct dum_ch_setup *ch_setup) |
| 81 | { |
| 82 | u32 r = nof_pixels_dxy(ch_setup); |
| 83 | switch (ch_setup->format) { |
| 84 | case RGB888: |
| 85 | case RGB666: |
| 86 | r *= 4; |
| 87 | break; |
| 88 | |
| 89 | default: |
| 90 | r *= 2; |
| 91 | break; |
| 92 | } |
| 93 | return r; |
| 94 | } |
| 95 | |
| 96 | static u32 build_command(int disp_no, u32 reg, u32 val) |
| 97 | { |
| 98 | return ((disp_no << 26) | BIT(25) | (val << 16) | (disp_no << 10) | |
| 99 | (reg << 0)); |
| 100 | } |
| 101 | |
| 102 | static u32 build_double_index(int disp_no, u32 val) |
| 103 | { |
| 104 | return ((disp_no << 26) | (val << 16) | (disp_no << 10) | (val << 0)); |
| 105 | } |
| 106 | |
| 107 | static void build_disp_window(struct dum_ch_setup * ch_setup, struct disp_window * dw) |
| 108 | { |
| 109 | dw->ymin = ch_setup->ymin; |
| 110 | dw->ymax = ch_setup->ymax; |
| 111 | dw->xmin_l = ch_setup->xmin & 0xFF; |
| 112 | dw->xmin_h = (ch_setup->xmin & BIT(8)) >> 8; |
| 113 | dw->xmax_l = ch_setup->xmax & 0xFF; |
| 114 | dw->xmax_h = (ch_setup->xmax & BIT(8)) >> 8; |
| 115 | } |
| 116 | |
| 117 | static int put_channel(struct dumchannel chan) |
| 118 | { |
| 119 | int i = chan.channelnr; |
| 120 | |
| 121 | if (i < 0 || i > MAX_DUM_CHANNELS) |
| 122 | return -EINVAL; |
| 123 | else { |
| 124 | DUM_CH_MIN(i) = chan.dum_ch_min; |
| 125 | DUM_CH_MAX(i) = chan.dum_ch_max; |
| 126 | DUM_CH_CONF(i) = chan.dum_ch_conf; |
| 127 | DUM_CH_CTRL(i) = chan.dum_ch_ctrl; |
| 128 | } |
| 129 | |
| 130 | return 0; |
| 131 | } |
| 132 | |
| 133 | static void clear_channel(int channr) |
| 134 | { |
| 135 | struct dumchannel chan; |
| 136 | |
| 137 | chan.channelnr = channr; |
| 138 | chan.dum_ch_min = 0; |
| 139 | chan.dum_ch_max = 0; |
| 140 | chan.dum_ch_conf = 0; |
| 141 | chan.dum_ch_ctrl = 0; |
| 142 | |
| 143 | put_channel(chan); |
| 144 | } |
| 145 | |
| 146 | static int put_cmd_string(struct cmdstring cmds) |
| 147 | { |
| 148 | u16 *cmd_str_virtaddr; |
| 149 | u32 *cmd_ptr0_virtaddr; |
| 150 | u32 cmd_str_physaddr; |
| 151 | |
| 152 | int i = cmds.channelnr; |
| 153 | |
| 154 | if (i < 0 || i > MAX_DUM_CHANNELS) |
| 155 | return -EINVAL; |
| 156 | else if ((cmd_ptr0_virtaddr = |
| 157 | (int *)ioremap_nocache(DUM_COM_BASE, |
| 158 | sizeof(int) * MAX_DUM_CHANNELS)) == |
| 159 | NULL) |
| 160 | return -EIOREMAPFAILED; |
| 161 | else { |
| 162 | cmd_str_physaddr = ioread32(&cmd_ptr0_virtaddr[cmds.channelnr]); |
| 163 | if ((cmd_str_virtaddr = |
| 164 | (u16 *) ioremap_nocache(cmd_str_physaddr, |
| 165 | sizeof(cmds))) == NULL) { |
| 166 | iounmap(cmd_ptr0_virtaddr); |
| 167 | return -EIOREMAPFAILED; |
| 168 | } else { |
| 169 | int t; |
| 170 | for (t = 0; t < 8; t++) |
| 171 | iowrite16(*((u16 *)&cmds.prestringlen + t), |
| 172 | cmd_str_virtaddr + t); |
| 173 | |
| 174 | for (t = 0; t < cmds.prestringlen / 2; t++) |
| 175 | iowrite16(*((u16 *)&cmds.precmd + t), |
| 176 | cmd_str_virtaddr + t + 8); |
| 177 | |
| 178 | for (t = 0; t < cmds.poststringlen / 2; t++) |
| 179 | iowrite16(*((u16 *)&cmds.postcmd + t), |
| 180 | cmd_str_virtaddr + t + 8 + |
| 181 | cmds.prestringlen / 2); |
| 182 | |
| 183 | iounmap(cmd_ptr0_virtaddr); |
| 184 | iounmap(cmd_str_virtaddr); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | return 0; |
| 189 | } |
| 190 | |
| 191 | static u32 dum_ch_setup(int ch_no, struct dum_ch_setup * ch_setup) |
| 192 | { |
| 193 | struct cmdstring cmds_c; |
| 194 | struct cmdstring *cmds = &cmds_c; |
| 195 | struct disp_window dw; |
| 196 | int standard; |
| 197 | u32 orientation = 0; |
| 198 | struct dumchannel chan = { 0 }; |
| 199 | int ret; |
| 200 | |
| 201 | if ((ch_setup->xmirror) || (ch_setup->ymirror) || (ch_setup->rotate)) { |
| 202 | standard = 0; |
| 203 | |
| 204 | orientation = BIT(1); /* always set 9-bit-bus */ |
| 205 | if (ch_setup->xmirror) |
| 206 | orientation |= BIT(4); |
| 207 | if (ch_setup->ymirror) |
| 208 | orientation |= BIT(3); |
| 209 | if (ch_setup->rotate) |
| 210 | orientation |= BIT(0); |
| 211 | } else |
| 212 | standard = 1; |
| 213 | |
| 214 | cmds->channelnr = ch_no; |
| 215 | |
| 216 | /* build command string header */ |
| 217 | if (standard) { |
| 218 | cmds->prestringlen = 32; |
| 219 | cmds->poststringlen = 0; |
| 220 | } else { |
| 221 | cmds->prestringlen = 48; |
| 222 | cmds->poststringlen = 16; |
| 223 | } |
| 224 | |
| 225 | cmds->format = |
| 226 | (u16) ((ch_setup->disp_no << 4) | (BIT(3)) | (ch_setup->format)); |
| 227 | cmds->reserved = 0x0; |
| 228 | cmds->startaddr_low = (ch_setup->minadr & 0xFFFF); |
| 229 | cmds->startaddr_high = (ch_setup->minadr >> 16); |
| 230 | |
| 231 | if ((ch_setup->minadr == 0) && (ch_setup->maxadr == 0) |
| 232 | && (ch_setup->xmin == 0) |
| 233 | && (ch_setup->ymin == 0) && (ch_setup->xmax == 0) |
| 234 | && (ch_setup->ymax == 0)) { |
| 235 | cmds->pixdatlen_low = 0; |
| 236 | cmds->pixdatlen_high = 0; |
| 237 | } else { |
| 238 | u32 nbytes = nof_bytes(ch_setup); |
| 239 | cmds->pixdatlen_low = (nbytes & 0xFFFF); |
| 240 | cmds->pixdatlen_high = (nbytes >> 16); |
| 241 | } |
| 242 | |
| 243 | if (ch_setup->slave_trans) |
| 244 | cmds->pixdatlen_high |= BIT(15); |
| 245 | |
| 246 | /* build pre-string */ |
| 247 | build_disp_window(ch_setup, &dw); |
| 248 | |
| 249 | if (standard) { |
| 250 | cmds->precmd[0] = |
| 251 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, 0x99); |
| 252 | cmds->precmd[1] = |
| 253 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, |
| 254 | dw.xmin_l); |
| 255 | cmds->precmd[2] = |
| 256 | build_command(ch_setup->disp_no, DISP_XMIN_H_REG, |
| 257 | dw.xmin_h); |
| 258 | cmds->precmd[3] = |
| 259 | build_command(ch_setup->disp_no, DISP_YMIN_REG, dw.ymin); |
| 260 | cmds->precmd[4] = |
| 261 | build_command(ch_setup->disp_no, DISP_XMAX_L_REG, |
| 262 | dw.xmax_l); |
| 263 | cmds->precmd[5] = |
| 264 | build_command(ch_setup->disp_no, DISP_XMAX_H_REG, |
| 265 | dw.xmax_h); |
| 266 | cmds->precmd[6] = |
| 267 | build_command(ch_setup->disp_no, DISP_YMAX_REG, dw.ymax); |
| 268 | cmds->precmd[7] = |
| 269 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); |
| 270 | } else { |
| 271 | if (dw.xmin_l == ch_no) |
| 272 | cmds->precmd[0] = |
| 273 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, |
| 274 | 0x99); |
| 275 | else |
| 276 | cmds->precmd[0] = |
| 277 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, |
| 278 | ch_no); |
| 279 | |
| 280 | cmds->precmd[1] = |
| 281 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, |
| 282 | dw.xmin_l); |
| 283 | cmds->precmd[2] = |
| 284 | build_command(ch_setup->disp_no, DISP_XMIN_H_REG, |
| 285 | dw.xmin_h); |
| 286 | cmds->precmd[3] = |
| 287 | build_command(ch_setup->disp_no, DISP_YMIN_REG, dw.ymin); |
| 288 | cmds->precmd[4] = |
| 289 | build_command(ch_setup->disp_no, DISP_XMAX_L_REG, |
| 290 | dw.xmax_l); |
| 291 | cmds->precmd[5] = |
| 292 | build_command(ch_setup->disp_no, DISP_XMAX_H_REG, |
| 293 | dw.xmax_h); |
| 294 | cmds->precmd[6] = |
| 295 | build_command(ch_setup->disp_no, DISP_YMAX_REG, dw.ymax); |
| 296 | cmds->precmd[7] = |
| 297 | build_command(ch_setup->disp_no, DISP_1_REG, orientation); |
| 298 | cmds->precmd[8] = |
| 299 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); |
| 300 | cmds->precmd[9] = |
| 301 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); |
| 302 | cmds->precmd[0xA] = |
| 303 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); |
| 304 | cmds->precmd[0xB] = |
| 305 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); |
| 306 | cmds->postcmd[0] = |
| 307 | build_command(ch_setup->disp_no, DISP_1_REG, BIT(1)); |
| 308 | cmds->postcmd[1] = |
| 309 | build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 1); |
| 310 | cmds->postcmd[2] = |
| 311 | build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 2); |
| 312 | cmds->postcmd[3] = |
| 313 | build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 3); |
| 314 | } |
| 315 | |
| 316 | if ((ret = put_cmd_string(cmds_c)) != 0) { |
| 317 | return ret; |
| 318 | } |
| 319 | |
| 320 | chan.channelnr = cmds->channelnr; |
| 321 | chan.dum_ch_min = ch_setup->dirtybuffer + ch_setup->minadr; |
| 322 | chan.dum_ch_max = ch_setup->dirtybuffer + ch_setup->maxadr; |
| 323 | chan.dum_ch_conf = 0x002; |
| 324 | chan.dum_ch_ctrl = 0x04; |
| 325 | |
| 326 | put_channel(chan); |
| 327 | |
| 328 | return 0; |
| 329 | } |
| 330 | |
| 331 | static u32 display_open(int ch_no, int auto_update, u32 * dirty_buffer, |
| 332 | u32 * frame_buffer, u32 xpos, u32 ypos, u32 w, u32 h) |
| 333 | { |
| 334 | |
| 335 | struct dum_ch_setup k; |
| 336 | int ret; |
| 337 | |
| 338 | /* keep width & height within display area */ |
| 339 | if ((xpos + w) > DISP_MAX_X_SIZE) |
| 340 | w = DISP_MAX_X_SIZE - xpos; |
| 341 | |
| 342 | if ((ypos + h) > DISP_MAX_Y_SIZE) |
| 343 | h = DISP_MAX_Y_SIZE - ypos; |
| 344 | |
| 345 | /* assume 1 display only */ |
| 346 | k.disp_no = 0; |
| 347 | k.xmin = xpos; |
| 348 | k.ymin = ypos; |
| 349 | k.xmax = xpos + (w - 1); |
| 350 | k.ymax = ypos + (h - 1); |
| 351 | |
| 352 | /* adjust min and max values if necessary */ |
| 353 | if (k.xmin > DISP_MAX_X_SIZE - 1) |
| 354 | k.xmin = DISP_MAX_X_SIZE - 1; |
| 355 | if (k.ymin > DISP_MAX_Y_SIZE - 1) |
| 356 | k.ymin = DISP_MAX_Y_SIZE - 1; |
| 357 | |
| 358 | if (k.xmax > DISP_MAX_X_SIZE - 1) |
| 359 | k.xmax = DISP_MAX_X_SIZE - 1; |
| 360 | if (k.ymax > DISP_MAX_Y_SIZE - 1) |
| 361 | k.ymax = DISP_MAX_Y_SIZE - 1; |
| 362 | |
| 363 | k.xmirror = 0; |
| 364 | k.ymirror = 0; |
| 365 | k.rotate = 0; |
| 366 | k.minadr = (u32) frame_buffer; |
| 367 | k.maxadr = (u32) frame_buffer + (((w - 1) << 10) | ((h << 2) - 2)); |
| 368 | k.pad = PAD_1024; |
| 369 | k.dirtybuffer = (u32) dirty_buffer; |
| 370 | k.format = RGB888; |
| 371 | k.hwdirty = 0; |
| 372 | k.slave_trans = 0; |
| 373 | |
| 374 | ret = dum_ch_setup(ch_no, &k); |
| 375 | |
| 376 | return ret; |
| 377 | } |
| 378 | |
| 379 | static void lcd_reset(void) |
| 380 | { |
| 381 | u32 *dum_pio_base = (u32 *)IO_ADDRESS(PNX4008_PIO_BASE); |
| 382 | |
| 383 | udelay(1); |
| 384 | iowrite32(BIT(19), &dum_pio_base[2]); |
| 385 | udelay(1); |
| 386 | iowrite32(BIT(19), &dum_pio_base[1]); |
| 387 | udelay(1); |
| 388 | } |
| 389 | |
| 390 | static int dum_init(struct platform_device *pdev) |
| 391 | { |
| 392 | struct clk *clk; |
| 393 | |
| 394 | /* enable DUM clock */ |
| 395 | clk = clk_get(&pdev->dev, "dum_ck"); |
| 396 | if (IS_ERR(clk)) { |
| 397 | printk(KERN_ERR "pnx4008_dum: Unable to access DUM clock\n"); |
| 398 | return PTR_ERR(clk); |
| 399 | } |
| 400 | |
| 401 | clk_set_rate(clk, 1); |
| 402 | clk_put(clk); |
| 403 | |
| 404 | DUM_CTRL = V_DUM_RESET; |
| 405 | |
| 406 | /* set priority to "round-robin". All other params to "false" */ |
| 407 | DUM_CONF = BIT(9); |
| 408 | |
| 409 | /* Display 1 */ |
| 410 | DUM_WTCFG1 = PNX4008_DUM_WT_CFG; |
| 411 | DUM_RTCFG1 = PNX4008_DUM_RT_CFG; |
| 412 | DUM_TCFG = PNX4008_DUM_T_CFG; |
| 413 | |
| 414 | return 0; |
| 415 | } |
| 416 | |
| 417 | static void dum_chan_init(void) |
| 418 | { |
| 419 | int i = 0, ch = 0; |
| 420 | u32 *cmdptrs; |
| 421 | u32 *cmdstrings; |
| 422 | |
| 423 | DUM_COM_BASE = |
| 424 | CMDSTRING_BASEADDR + BYTES_PER_CMDSTRING * NR_OF_CMDSTRINGS; |
| 425 | |
| 426 | if ((cmdptrs = |
| 427 | (u32 *) ioremap_nocache(DUM_COM_BASE, |
| 428 | sizeof(u32) * NR_OF_CMDSTRINGS)) == NULL) |
| 429 | return; |
| 430 | |
| 431 | for (ch = 0; ch < NR_OF_CMDSTRINGS; ch++) |
| 432 | iowrite32(CMDSTRING_BASEADDR + BYTES_PER_CMDSTRING * ch, |
| 433 | cmdptrs + ch); |
| 434 | |
| 435 | for (ch = 0; ch < MAX_DUM_CHANNELS; ch++) |
| 436 | clear_channel(ch); |
| 437 | |
| 438 | /* Clear the cmdstrings */ |
| 439 | cmdstrings = |
| 440 | (u32 *)ioremap_nocache(*cmdptrs, |
| 441 | BYTES_PER_CMDSTRING * NR_OF_CMDSTRINGS); |
| 442 | |
| 443 | if (!cmdstrings) |
| 444 | goto out; |
| 445 | |
| 446 | for (i = 0; i < NR_OF_CMDSTRINGS * BYTES_PER_CMDSTRING / sizeof(u32); |
| 447 | i++) |
| 448 | iowrite32(0, cmdstrings + i); |
| 449 | |
| 450 | iounmap((u32 *)cmdstrings); |
| 451 | |
| 452 | out: |
| 453 | iounmap((u32 *)cmdptrs); |
| 454 | } |
| 455 | |
| 456 | static void lcd_init(void) |
| 457 | { |
| 458 | lcd_reset(); |
| 459 | |
| 460 | DUM_OUTP_FORMAT1 = 0; /* RGB666 */ |
| 461 | |
| 462 | udelay(1); |
| 463 | iowrite32(V_LCD_STANDBY_OFF, dum_data.slave_virt_base); |
| 464 | udelay(1); |
| 465 | iowrite32(V_LCD_USE_9BIT_BUS, dum_data.slave_virt_base); |
| 466 | udelay(1); |
| 467 | iowrite32(V_LCD_SYNC_RISE_L, dum_data.slave_virt_base); |
| 468 | udelay(1); |
| 469 | iowrite32(V_LCD_SYNC_RISE_H, dum_data.slave_virt_base); |
| 470 | udelay(1); |
| 471 | iowrite32(V_LCD_SYNC_FALL_L, dum_data.slave_virt_base); |
| 472 | udelay(1); |
| 473 | iowrite32(V_LCD_SYNC_FALL_H, dum_data.slave_virt_base); |
| 474 | udelay(1); |
| 475 | iowrite32(V_LCD_SYNC_ENABLE, dum_data.slave_virt_base); |
| 476 | udelay(1); |
| 477 | iowrite32(V_LCD_DISPLAY_ON, dum_data.slave_virt_base); |
| 478 | udelay(1); |
| 479 | } |
| 480 | |
| 481 | /* Interface exported to framebuffer drivers */ |
| 482 | |
| 483 | int pnx4008_get_fb_addresses(int fb_type, void **virt_addr, |
| 484 | dma_addr_t *phys_addr, int *fb_length) |
| 485 | { |
| 486 | int i; |
| 487 | int ret = -1; |
| 488 | for (i = 0; i < ARRAY_SIZE(fb_addr); i++) |
| 489 | if (fb_addr[i].fb_type == fb_type) { |
| 490 | *virt_addr = (void *)(dum_data.lcd_virt_start + |
| 491 | fb_addr[i].addr_offset); |
| 492 | *phys_addr = |
| 493 | dum_data.lcd_phys_start + fb_addr[i].addr_offset; |
| 494 | *fb_length = fb_addr[i].fb_length; |
| 495 | ret = 0; |
| 496 | break; |
| 497 | } |
| 498 | |
| 499 | return ret; |
| 500 | } |
| 501 | |
| 502 | EXPORT_SYMBOL(pnx4008_get_fb_addresses); |
| 503 | |
| 504 | int pnx4008_alloc_dum_channel(int dev_id) |
| 505 | { |
| 506 | int i = 0; |
| 507 | |
| 508 | while ((i < MAX_DUM_CHANNELS) && (dum_data.fb_owning_channel[i] != -1)) |
| 509 | i++; |
| 510 | |
| 511 | if (i == MAX_DUM_CHANNELS) |
| 512 | return -ENORESOURCESLEFT; |
| 513 | else { |
| 514 | dum_data.fb_owning_channel[i] = dev_id; |
| 515 | return i; |
| 516 | } |
| 517 | } |
| 518 | |
| 519 | EXPORT_SYMBOL(pnx4008_alloc_dum_channel); |
| 520 | |
| 521 | int pnx4008_free_dum_channel(int channr, int dev_id) |
| 522 | { |
| 523 | if (channr < 0 || channr > MAX_DUM_CHANNELS) |
| 524 | return -EINVAL; |
| 525 | else if (dum_data.fb_owning_channel[channr] != dev_id) |
| 526 | return -EFBNOTOWNER; |
| 527 | else { |
| 528 | clear_channel(channr); |
| 529 | dum_data.fb_owning_channel[channr] = -1; |
| 530 | } |
| 531 | |
| 532 | return 0; |
| 533 | } |
| 534 | |
| 535 | EXPORT_SYMBOL(pnx4008_free_dum_channel); |
| 536 | |
| 537 | int pnx4008_put_dum_channel_uf(struct dumchannel_uf chan_uf, int dev_id) |
| 538 | { |
| 539 | int i = chan_uf.channelnr; |
| 540 | int ret; |
| 541 | |
| 542 | if (i < 0 || i > MAX_DUM_CHANNELS) |
| 543 | return -EINVAL; |
| 544 | else if (dum_data.fb_owning_channel[i] != dev_id) |
| 545 | return -EFBNOTOWNER; |
| 546 | else if ((ret = |
| 547 | display_open(chan_uf.channelnr, 0, chan_uf.dirty, |
| 548 | chan_uf.source, chan_uf.y_offset, |
| 549 | chan_uf.x_offset, chan_uf.height, |
| 550 | chan_uf.width)) != 0) |
| 551 | return ret; |
| 552 | else { |
| 553 | dum_data.chan_uf_store[i].dirty = chan_uf.dirty; |
| 554 | dum_data.chan_uf_store[i].source = chan_uf.source; |
| 555 | dum_data.chan_uf_store[i].x_offset = chan_uf.x_offset; |
| 556 | dum_data.chan_uf_store[i].y_offset = chan_uf.y_offset; |
| 557 | dum_data.chan_uf_store[i].width = chan_uf.width; |
| 558 | dum_data.chan_uf_store[i].height = chan_uf.height; |
| 559 | } |
| 560 | |
| 561 | return 0; |
| 562 | } |
| 563 | |
| 564 | EXPORT_SYMBOL(pnx4008_put_dum_channel_uf); |
| 565 | |
| 566 | int pnx4008_set_dum_channel_sync(int channr, int val, int dev_id) |
| 567 | { |
| 568 | if (channr < 0 || channr > MAX_DUM_CHANNELS) |
| 569 | return -EINVAL; |
| 570 | else if (dum_data.fb_owning_channel[channr] != dev_id) |
| 571 | return -EFBNOTOWNER; |
| 572 | else { |
| 573 | if (val == CONF_SYNC_ON) { |
| 574 | DUM_CH_CONF(channr) |= CONF_SYNCENABLE; |
| 575 | DUM_CH_CONF(channr) |= DUM_CHANNEL_CFG_SYNC_MASK | |
| 576 | DUM_CHANNEL_CFG_SYNC_MASK_SET; |
| 577 | } else if (val == CONF_SYNC_OFF) |
| 578 | DUM_CH_CONF(channr) &= ~CONF_SYNCENABLE; |
| 579 | else |
| 580 | return -EINVAL; |
| 581 | } |
| 582 | |
| 583 | return 0; |
| 584 | } |
| 585 | |
| 586 | EXPORT_SYMBOL(pnx4008_set_dum_channel_sync); |
| 587 | |
| 588 | int pnx4008_set_dum_channel_dirty_detect(int channr, int val, int dev_id) |
| 589 | { |
| 590 | if (channr < 0 || channr > MAX_DUM_CHANNELS) |
| 591 | return -EINVAL; |
| 592 | else if (dum_data.fb_owning_channel[channr] != dev_id) |
| 593 | return -EFBNOTOWNER; |
| 594 | else { |
| 595 | if (val == CONF_DIRTYDETECTION_ON) |
| 596 | DUM_CH_CONF(channr) |= CONF_DIRTYENABLE; |
| 597 | else if (val == CONF_DIRTYDETECTION_OFF) |
| 598 | DUM_CH_CONF(channr) &= ~CONF_DIRTYENABLE; |
| 599 | else |
| 600 | return -EINVAL; |
| 601 | } |
| 602 | |
| 603 | return 0; |
| 604 | } |
| 605 | |
| 606 | EXPORT_SYMBOL(pnx4008_set_dum_channel_dirty_detect); |
| 607 | |
| 608 | #if 0 /* Functions not used currently, but likely to be used in future */ |
| 609 | |
| 610 | static int get_channel(struct dumchannel *p_chan) |
| 611 | { |
| 612 | int i = p_chan->channelnr; |
| 613 | |
| 614 | if (i < 0 || i > MAX_DUM_CHANNELS) |
| 615 | return -EINVAL; |
| 616 | else { |
| 617 | p_chan->dum_ch_min = DUM_CH_MIN(i); |
| 618 | p_chan->dum_ch_max = DUM_CH_MAX(i); |
| 619 | p_chan->dum_ch_conf = DUM_CH_CONF(i); |
| 620 | p_chan->dum_ch_stat = DUM_CH_STAT(i); |
| 621 | p_chan->dum_ch_ctrl = 0; /* WriteOnly control register */ |
| 622 | } |
| 623 | |
| 624 | return 0; |
| 625 | } |
| 626 | |
| 627 | int pnx4008_get_dum_channel_uf(struct dumchannel_uf *p_chan_uf, int dev_id) |
| 628 | { |
| 629 | int i = p_chan_uf->channelnr; |
| 630 | |
| 631 | if (i < 0 || i > MAX_DUM_CHANNELS) |
| 632 | return -EINVAL; |
| 633 | else if (dum_data.fb_owning_channel[i] != dev_id) |
| 634 | return -EFBNOTOWNER; |
| 635 | else { |
| 636 | p_chan_uf->dirty = dum_data.chan_uf_store[i].dirty; |
| 637 | p_chan_uf->source = dum_data.chan_uf_store[i].source; |
| 638 | p_chan_uf->x_offset = dum_data.chan_uf_store[i].x_offset; |
| 639 | p_chan_uf->y_offset = dum_data.chan_uf_store[i].y_offset; |
| 640 | p_chan_uf->width = dum_data.chan_uf_store[i].width; |
| 641 | p_chan_uf->height = dum_data.chan_uf_store[i].height; |
| 642 | } |
| 643 | |
| 644 | return 0; |
| 645 | } |
| 646 | |
| 647 | EXPORT_SYMBOL(pnx4008_get_dum_channel_uf); |
| 648 | |
| 649 | int pnx4008_get_dum_channel_config(int channr, int dev_id) |
| 650 | { |
| 651 | int ret; |
| 652 | struct dumchannel chan; |
| 653 | |
| 654 | if (channr < 0 || channr > MAX_DUM_CHANNELS) |
| 655 | return -EINVAL; |
| 656 | else if (dum_data.fb_owning_channel[channr] != dev_id) |
| 657 | return -EFBNOTOWNER; |
| 658 | else { |
| 659 | chan.channelnr = channr; |
| 660 | if ((ret = get_channel(&chan)) != 0) |
| 661 | return ret; |
| 662 | } |
| 663 | |
| 664 | return (chan.dum_ch_conf & DUM_CHANNEL_CFG_MASK); |
| 665 | } |
| 666 | |
| 667 | EXPORT_SYMBOL(pnx4008_get_dum_channel_config); |
| 668 | |
| 669 | int pnx4008_force_update_dum_channel(int channr, int dev_id) |
| 670 | { |
| 671 | if (channr < 0 || channr > MAX_DUM_CHANNELS) |
| 672 | return -EINVAL; |
| 673 | |
| 674 | else if (dum_data.fb_owning_channel[channr] != dev_id) |
| 675 | return -EFBNOTOWNER; |
| 676 | else |
| 677 | DUM_CH_CTRL(channr) = CTRL_SETDIRTY; |
| 678 | |
| 679 | return 0; |
| 680 | } |
| 681 | |
| 682 | EXPORT_SYMBOL(pnx4008_force_update_dum_channel); |
| 683 | |
| 684 | #endif |
| 685 | |
| 686 | int pnx4008_sdum_mmap(struct fb_info *info, struct vm_area_struct *vma, |
| 687 | struct device *dev) |
| 688 | { |
| 689 | unsigned long off = vma->vm_pgoff << PAGE_SHIFT; |
| 690 | |
| 691 | if (off < info->fix.smem_len) { |
| 692 | vma->vm_pgoff += 1; |
| 693 | return dma_mmap_writecombine(dev, vma, |
| 694 | (void *)dum_data.lcd_virt_start, |
| 695 | dum_data.lcd_phys_start, |
| 696 | FB_DMA_SIZE); |
| 697 | } |
| 698 | return -EINVAL; |
| 699 | } |
| 700 | |
| 701 | EXPORT_SYMBOL(pnx4008_sdum_mmap); |
| 702 | |
| 703 | int pnx4008_set_dum_exit_notification(int dev_id) |
| 704 | { |
| 705 | int i; |
| 706 | |
| 707 | for (i = 0; i < MAX_DUM_CHANNELS; i++) |
| 708 | if (dum_data.fb_owning_channel[i] == dev_id) |
| 709 | return -ERESOURCESNOTFREED; |
| 710 | |
| 711 | return 0; |
| 712 | } |
| 713 | |
| 714 | EXPORT_SYMBOL(pnx4008_set_dum_exit_notification); |
| 715 | |
| 716 | /* Platform device driver for DUM */ |
| 717 | |
| 718 | static int sdum_suspend(struct platform_device *pdev, pm_message_t state) |
| 719 | { |
| 720 | int retval = 0; |
| 721 | struct clk *clk; |
| 722 | |
| 723 | clk = clk_get(0, "dum_ck"); |
| 724 | if (!IS_ERR(clk)) { |
| 725 | clk_set_rate(clk, 0); |
| 726 | clk_put(clk); |
| 727 | } else |
| 728 | retval = PTR_ERR(clk); |
| 729 | |
| 730 | /* disable BAC */ |
| 731 | DUM_CTRL = V_BAC_DISABLE_IDLE; |
| 732 | |
| 733 | /* LCD standby & turn off display */ |
| 734 | lcd_reset(); |
| 735 | |
| 736 | return retval; |
| 737 | } |
| 738 | |
| 739 | static int sdum_resume(struct platform_device *pdev) |
| 740 | { |
| 741 | int retval = 0; |
| 742 | struct clk *clk; |
| 743 | |
| 744 | clk = clk_get(0, "dum_ck"); |
| 745 | if (!IS_ERR(clk)) { |
| 746 | clk_set_rate(clk, 1); |
| 747 | clk_put(clk); |
| 748 | } else |
| 749 | retval = PTR_ERR(clk); |
| 750 | |
| 751 | /* wait for BAC disable */ |
| 752 | DUM_CTRL = V_BAC_DISABLE_TRIG; |
| 753 | |
| 754 | while (DUM_CTRL & BAC_ENABLED) |
| 755 | udelay(10); |
| 756 | |
| 757 | /* re-init LCD */ |
| 758 | lcd_init(); |
| 759 | |
| 760 | /* enable BAC and reset MUX */ |
| 761 | DUM_CTRL = V_BAC_ENABLE; |
| 762 | udelay(1); |
| 763 | DUM_CTRL = V_MUX_RESET; |
| 764 | return 0; |
| 765 | } |
| 766 | |
| 767 | static int __devinit sdum_probe(struct platform_device *pdev) |
| 768 | { |
| 769 | int ret = 0, i = 0; |
| 770 | |
| 771 | /* map frame buffer */ |
| 772 | dum_data.lcd_virt_start = (u32) dma_alloc_writecombine(&pdev->dev, |
| 773 | FB_DMA_SIZE, |
| 774 | &dum_data.lcd_phys_start, |
| 775 | GFP_KERNEL); |
| 776 | |
| 777 | if (!dum_data.lcd_virt_start) { |
| 778 | ret = -ENOMEM; |
| 779 | goto out_3; |
| 780 | } |
| 781 | |
| 782 | /* map slave registers */ |
| 783 | dum_data.slave_phys_base = PNX4008_DUM_SLAVE_BASE; |
| 784 | dum_data.slave_virt_base = |
| 785 | (u32 *) ioremap_nocache(dum_data.slave_phys_base, sizeof(u32)); |
| 786 | |
| 787 | if (dum_data.slave_virt_base == NULL) { |
| 788 | ret = -ENOMEM; |
| 789 | goto out_2; |
| 790 | } |
| 791 | |
| 792 | /* initialize DUM and LCD display */ |
| 793 | ret = dum_init(pdev); |
| 794 | if (ret) |
| 795 | goto out_1; |
| 796 | |
| 797 | dum_chan_init(); |
| 798 | lcd_init(); |
| 799 | |
| 800 | DUM_CTRL = V_BAC_ENABLE; |
| 801 | udelay(1); |
| 802 | DUM_CTRL = V_MUX_RESET; |
| 803 | |
| 804 | /* set decode address and sync clock divider */ |
| 805 | DUM_DECODE = dum_data.lcd_phys_start & DUM_DECODE_MASK; |
| 806 | DUM_CLK_DIV = PNX4008_DUM_CLK_DIV; |
| 807 | |
| 808 | for (i = 0; i < MAX_DUM_CHANNELS; i++) |
| 809 | dum_data.fb_owning_channel[i] = -1; |
| 810 | |
| 811 | /*setup wakeup interrupt */ |
| 812 | start_int_set_rising_edge(SE_DISP_SYNC_INT); |
| 813 | start_int_ack(SE_DISP_SYNC_INT); |
| 814 | start_int_umask(SE_DISP_SYNC_INT); |
| 815 | |
| 816 | return 0; |
| 817 | |
| 818 | out_1: |
| 819 | iounmap((void *)dum_data.slave_virt_base); |
| 820 | out_2: |
| 821 | dma_free_writecombine(&pdev->dev, FB_DMA_SIZE, |
| 822 | (void *)dum_data.lcd_virt_start, |
| 823 | dum_data.lcd_phys_start); |
| 824 | out_3: |
| 825 | return ret; |
| 826 | } |
| 827 | |
| 828 | static int sdum_remove(struct platform_device *pdev) |
| 829 | { |
| 830 | struct clk *clk; |
| 831 | |
| 832 | start_int_mask(SE_DISP_SYNC_INT); |
| 833 | |
| 834 | clk = clk_get(0, "dum_ck"); |
| 835 | if (!IS_ERR(clk)) { |
| 836 | clk_set_rate(clk, 0); |
| 837 | clk_put(clk); |
| 838 | } |
| 839 | |
| 840 | iounmap((void *)dum_data.slave_virt_base); |
| 841 | |
| 842 | dma_free_writecombine(&pdev->dev, FB_DMA_SIZE, |
| 843 | (void *)dum_data.lcd_virt_start, |
| 844 | dum_data.lcd_phys_start); |
| 845 | |
| 846 | return 0; |
| 847 | } |
| 848 | |
| 849 | static struct platform_driver sdum_driver = { |
| 850 | .driver = { |
| 851 | .name = "sdum", |
| 852 | }, |
| 853 | .probe = sdum_probe, |
| 854 | .remove = sdum_remove, |
| 855 | .suspend = sdum_suspend, |
| 856 | .resume = sdum_resume, |
| 857 | }; |
| 858 | |
| 859 | int __init sdum_init(void) |
| 860 | { |
| 861 | return platform_driver_register(&sdum_driver); |
| 862 | } |
| 863 | |
| 864 | static void __exit sdum_exit(void) |
| 865 | { |
| 866 | platform_driver_unregister(&sdum_driver); |
| 867 | }; |
| 868 | |
| 869 | module_init(sdum_init); |
| 870 | module_exit(sdum_exit); |
| 871 | |
| 872 | MODULE_LICENSE("GPL"); |