blob: 9ee55ccb68d9686d965630113a070b206307aaea [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/mutex.h>
16#include <linux/delay.h>
17
18#include "msm_fb.h"
19#include "tvenc.h"
20#include "external_common.h"
21
22#define TVOUT_HPD_DUTY_CYCLE 3000
23
24#define TV_DIMENSION_MAX_WIDTH 720
25#define TV_DIMENSION_MAX_HEIGHT 576
26
27struct tvout_msm_state_type {
28 struct external_common_state_type common;
29 struct platform_device *pdev;
30 struct timer_list hpd_state_timer;
31 struct timer_list hpd_work_timer;
32 struct work_struct hpd_work;
33 uint32 hpd_int_status;
34 uint32 prev_hpd_int_status;
35 uint32 five_retry;
36 int irq;
37 uint16 y_res;
38 boolean hpd_initialized;
39 boolean disp_powered_up;
40#ifdef CONFIG_SUSPEND
41 boolean pm_suspended;
42#endif
43
44};
45
46static struct tvout_msm_state_type *tvout_msm_state;
47static DEFINE_MUTEX(tvout_msm_state_mutex);
48
49static int tvout_off(struct platform_device *pdev);
50static int tvout_on(struct platform_device *pdev);
51static void tvout_check_status(void);
52
53static void tvout_msm_turn_on(boolean power_on)
54{
55 uint32 reg_val = 0;
56 reg_val = TV_IN(TV_ENC_CTL);
57 if (power_on) {
58 DEV_DBG("%s: TV Encoder turned on\n", __func__);
59 reg_val |= TVENC_CTL_ENC_EN;
60 } else {
61 DEV_DBG("%s: TV Encoder turned off\n", __func__);
62 reg_val = 0;
63 }
64 /* Enable TV Encoder*/
65 TV_OUT(TV_ENC_CTL, reg_val);
66}
67
68static void tvout_check_status()
69{
70 tvout_msm_state->hpd_int_status &= 0x05;
71 /* hpd_int_status could either be 0x05 or 0x04 for a cable
72 plug-out event when cable detect is driven by polling. */
73 if ((((tvout_msm_state->hpd_int_status == 0x05) ||
74 (tvout_msm_state->hpd_int_status == 0x04)) &&
75 (tvout_msm_state->prev_hpd_int_status == BIT(2))) ||
76 ((tvout_msm_state->hpd_int_status == 0x01) &&
77 (tvout_msm_state->prev_hpd_int_status == BIT(0)))) {
78 DEV_DBG("%s: cable event sent already!", __func__);
79 return;
80 }
81
82 if (tvout_msm_state->hpd_int_status & BIT(2)) {
83 DEV_DBG("%s: cable plug-out\n", __func__);
84 mutex_lock(&external_common_state_hpd_mutex);
85 external_common_state->hpd_state = FALSE;
86 mutex_unlock(&external_common_state_hpd_mutex);
87 kobject_uevent(external_common_state->uevent_kobj,
88 KOBJ_OFFLINE);
89 tvout_msm_state->prev_hpd_int_status = BIT(2);
90 } else if (tvout_msm_state->hpd_int_status & BIT(0)) {
91 DEV_DBG("%s: cable plug-in\n", __func__);
92 mutex_lock(&external_common_state_hpd_mutex);
93 external_common_state->hpd_state = TRUE;
94 mutex_unlock(&external_common_state_hpd_mutex);
95 kobject_uevent(external_common_state->uevent_kobj,
96 KOBJ_ONLINE);
97 tvout_msm_state->prev_hpd_int_status = BIT(0);
98 }
99}
100
101/* ISR for TV out cable detect */
102static irqreturn_t tvout_msm_isr(int irq, void *dev_id)
103{
104 tvout_msm_state->hpd_int_status = TV_IN(TV_INTR_STATUS);
105 TV_OUT(TV_INTR_CLEAR, tvout_msm_state->hpd_int_status);
106 DEV_DBG("%s: ISR: 0x%02x\n", __func__,
107 tvout_msm_state->hpd_int_status & 0x05);
108
109 if (tvenc_pdata->poll)
110 if (!tvout_msm_state || !tvout_msm_state->disp_powered_up) {
111 DEV_DBG("%s: ISR ignored, display not yet powered on\n",
112 __func__);
113 return IRQ_HANDLED;
114 }
115 if (tvout_msm_state->hpd_int_status & BIT(0) ||
116 tvout_msm_state->hpd_int_status & BIT(2)) {
117 /* Use .75sec to debounce the interrupt */
118 mod_timer(&tvout_msm_state->hpd_state_timer, jiffies
119 + msecs_to_jiffies(750));
120 }
121
122 return IRQ_HANDLED;
123}
124
125/* Interrupt debounce timer */
126static void tvout_msm_hpd_state_timer(unsigned long data)
127{
128#ifdef CONFIG_SUSPEND
129 mutex_lock(&tvout_msm_state_mutex);
130 if (tvout_msm_state->pm_suspended) {
131 mutex_unlock(&tvout_msm_state_mutex);
132 DEV_WARN("%s: ignored, pm_suspended\n", __func__);
133 return;
134 }
135 mutex_unlock(&tvout_msm_state_mutex);
136#endif
137
138 if (tvenc_pdata->poll)
139 if (!tvout_msm_state || !tvout_msm_state->disp_powered_up) {
140 DEV_DBG("%s: ignored, display powered off\n", __func__);
141 return;
142 }
143
144 /* TV_INTR_STATUS[0x204]
145 When a TV_ENC interrupt occurs, then reading this register will
146 indicate what caused the interrupt since that each bit indicates
147 the source of the interrupt that had happened. If multiple
148 interrupt sources had happened, then multiple bits of this
149 register will be set
150 Bit 0 : Load present on Video1
151 Bit 1 : Load present on Video2
152 Bit 2 : Load removed on Video1
153 Bit 3 : Load removed on Video2
154 */
155
156 /* Locking interrupt status is not required because
157 last status read after debouncing is used */
158 if ((tvout_msm_state->hpd_int_status & 0x05) == 0x05) {
159 /* SW-workaround :If the status read after debouncing is
160 0x05(indicating both load present & load removed- which can't
161 happen in reality), force an update. If status remains 0x05
162 after retry, it's a cable unplug event */
163 if (++tvout_msm_state->five_retry < 2) {
164 uint32 reg;
165 DEV_DBG("tvout: Timer: 0x05\n");
166 TV_OUT(TV_INTR_CLEAR, 0xf);
167 reg = TV_IN(TV_DAC_INTF);
168 TV_OUT(TV_DAC_INTF, reg & ~TVENC_LOAD_DETECT_EN);
169 TV_OUT(TV_INTR_CLEAR, 0xf);
170 reg = TV_IN(TV_DAC_INTF);
171 TV_OUT(TV_DAC_INTF, reg | TVENC_LOAD_DETECT_EN);
172 return;
173 }
174 }
175 tvout_msm_state->five_retry = 0;
176 tvout_check_status();
177}
178
179static void tvout_msm_hpd_work(struct work_struct *work)
180{
181 uint32 reg;
182
183#ifdef CONFIG_SUSPEND
184 mutex_lock(&tvout_msm_state_mutex);
185 if (tvout_msm_state->pm_suspended) {
186 mutex_unlock(&tvout_msm_state_mutex);
187 DEV_WARN("%s: ignored, pm_suspended\n", __func__);
188 return;
189 }
190 mutex_unlock(&tvout_msm_state_mutex);
191#endif
192
193 /* Enable power lines & clocks */
194 tvenc_pdata->pm_vid_en(1);
195 tvenc_set_clock(CLOCK_ON);
196
197 /* Enable encoder to get a stable interrupt */
198 reg = TV_IN(TV_ENC_CTL);
199 TV_OUT(TV_ENC_CTL, reg | TVENC_CTL_ENC_EN);
200
201 /* SW- workaround to update status register */
202 reg = TV_IN(TV_DAC_INTF);
203 TV_OUT(TV_DAC_INTF, reg & ~TVENC_LOAD_DETECT_EN);
204 TV_OUT(TV_INTR_CLEAR, 0xf);
205 reg = TV_IN(TV_DAC_INTF);
206 TV_OUT(TV_DAC_INTF, reg | TVENC_LOAD_DETECT_EN);
207
208 tvout_msm_state->hpd_int_status = TV_IN(TV_INTR_STATUS);
209
210 /* Disable TV encoder */
211 reg = TV_IN(TV_ENC_CTL);
212 TV_OUT(TV_ENC_CTL, reg & ~TVENC_CTL_ENC_EN);
213
214 /*Disable power lines & clocks */
215 tvenc_set_clock(CLOCK_OFF);
216 tvenc_pdata->pm_vid_en(0);
217
218 DEV_DBG("%s: ISR: 0x%02x\n", __func__,
219 tvout_msm_state->hpd_int_status & 0x05);
220
221 mod_timer(&tvout_msm_state->hpd_work_timer, jiffies
222 + msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE));
223
224 tvout_check_status();
225}
226
227static void tvout_msm_hpd_work_timer(unsigned long data)
228{
229 schedule_work(&tvout_msm_state->hpd_work);
230}
231
232static int tvout_on(struct platform_device *pdev)
233{
234 uint32 reg = 0;
235 struct fb_var_screeninfo *var;
236 struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
237
238 if (!mfd)
239 return -ENODEV;
240
241 if (mfd->key != MFD_KEY)
242 return -EINVAL;
243
244#ifdef CONFIG_SUSPEND
245 mutex_lock(&tvout_msm_state_mutex);
246 if (tvout_msm_state->pm_suspended) {
247 mutex_unlock(&tvout_msm_state_mutex);
248 DEV_WARN("%s: ignored, pm_suspended\n", __func__);
249 return -ENODEV;
250 }
251 mutex_unlock(&tvout_msm_state_mutex);
252#endif
253
254 var = &mfd->fbi->var;
255 if (var->reserved[3] >= NTSC_M && var->reserved[3] <= PAL_N)
256 external_common_state->video_resolution = var->reserved[3];
257
258 tvout_msm_state->pdev = pdev;
259 if (del_timer(&tvout_msm_state->hpd_work_timer))
260 DEV_DBG("%s: work timer stopped\n", __func__);
261
262 TV_OUT(TV_ENC_CTL, 0); /* disable TV encoder */
263
264 switch (external_common_state->video_resolution) {
265 case NTSC_M:
266 case NTSC_J:
267 TV_OUT(TV_CGMS, 0x0);
268 /* NTSC Timing */
269 TV_OUT(TV_SYNC_1, 0x0020009e);
270 TV_OUT(TV_SYNC_2, 0x011306B4);
271 TV_OUT(TV_SYNC_3, 0x0006000C);
272 TV_OUT(TV_SYNC_4, 0x0028020D);
273 TV_OUT(TV_SYNC_5, 0x005E02FB);
274 TV_OUT(TV_SYNC_6, 0x0006000C);
275 TV_OUT(TV_SYNC_7, 0x00000012);
276 TV_OUT(TV_BURST_V1, 0x0013020D);
277 TV_OUT(TV_BURST_V2, 0x0014020C);
278 TV_OUT(TV_BURST_V3, 0x0013020D);
279 TV_OUT(TV_BURST_V4, 0x0014020C);
280 TV_OUT(TV_BURST_H, 0x00AE00F2);
281 TV_OUT(TV_SOL_REQ_ODD, 0x00280208);
282 TV_OUT(TV_SOL_REQ_EVEN, 0x00290209);
283
284 reg |= TVENC_CTL_TV_MODE_NTSC_M_PAL60;
285
286 if (external_common_state->video_resolution == NTSC_M) {
287 /* Cr gain 11, Cb gain C6, y_gain 97 */
288 TV_OUT(TV_GAIN, 0x0081B697);
289 } else {
290 /* Cr gain 11, Cb gain C6, y_gain 97 */
291 TV_OUT(TV_GAIN, 0x008bc4a3);
292 reg |= TVENC_CTL_NTSCJ_MODE;
293 }
294
295 var->yres = 480;
296 break;
297 case PAL_BDGHIN:
298 case PAL_N:
299 /* PAL Timing */
300 TV_OUT(TV_SYNC_1, 0x00180097);
301 TV_OUT(TV_SYNC_3, 0x0005000a);
302 TV_OUT(TV_SYNC_4, 0x00320271);
303 TV_OUT(TV_SYNC_5, 0x005602f9);
304 TV_OUT(TV_SYNC_6, 0x0005000a);
305 TV_OUT(TV_SYNC_7, 0x0000000f);
306 TV_OUT(TV_BURST_V1, 0x0012026e);
307 TV_OUT(TV_BURST_V2, 0x0011026d);
308 TV_OUT(TV_BURST_V3, 0x00100270);
309 TV_OUT(TV_BURST_V4, 0x0013026f);
310 TV_OUT(TV_SOL_REQ_ODD, 0x0030026e);
311 TV_OUT(TV_SOL_REQ_EVEN, 0x0031026f);
312
313 if (external_common_state->video_resolution == PAL_BDGHIN) {
314 /* Cr gain 11, Cb gain C6, y_gain 97 */
315 TV_OUT(TV_GAIN, 0x0088c1a0);
316 TV_OUT(TV_CGMS, 0x00012345);
317 TV_OUT(TV_SYNC_2, 0x011f06c0);
318 TV_OUT(TV_BURST_H, 0x00af00ea);
319 reg |= TVENC_CTL_TV_MODE_PAL_BDGHIN;
320 } else {
321 /* Cr gain 11, Cb gain C6, y_gain 97 */
322 TV_OUT(TV_GAIN, 0x0081b697);
323 TV_OUT(TV_CGMS, 0x000af317);
324 TV_OUT(TV_SYNC_2, 0x12006c0);
325 TV_OUT(TV_BURST_H, 0x00af00fa);
326 reg |= TVENC_CTL_TV_MODE_PAL_N;
327 }
328 var->yres = 576;
329 break;
330 case PAL_M:
331 /* Cr gain 11, Cb gain C6, y_gain 97 */
332 TV_OUT(TV_GAIN, 0x0081b697);
333 TV_OUT(TV_CGMS, 0x000af317);
334 TV_OUT(TV_TEST_MUX, 0x000001c3);
335 TV_OUT(TV_TEST_MODE, 0x00000002);
336 /* PAL Timing */
337 TV_OUT(TV_SYNC_1, 0x0020009e);
338 TV_OUT(TV_SYNC_2, 0x011306b4);
339 TV_OUT(TV_SYNC_3, 0x0006000c);
340 TV_OUT(TV_SYNC_4, 0x0028020D);
341 TV_OUT(TV_SYNC_5, 0x005e02fb);
342 TV_OUT(TV_SYNC_6, 0x0006000c);
343 TV_OUT(TV_SYNC_7, 0x00000012);
344 TV_OUT(TV_BURST_V1, 0x0012020b);
345 TV_OUT(TV_BURST_V2, 0x0016020c);
346 TV_OUT(TV_BURST_V3, 0x00150209);
347 TV_OUT(TV_BURST_V4, 0x0013020c);
348 TV_OUT(TV_BURST_H, 0x00bf010b);
349 TV_OUT(TV_SOL_REQ_ODD, 0x00280208);
350 TV_OUT(TV_SOL_REQ_EVEN, 0x00290209);
351
352 reg |= TVENC_CTL_TV_MODE_PAL_M;
353 var->yres = 480;
354 break;
355 default:
356 return -ENODEV;
357 }
358
359 reg |= TVENC_CTL_Y_FILTER_EN | TVENC_CTL_CR_FILTER_EN |
360 TVENC_CTL_CB_FILTER_EN | TVENC_CTL_SINX_FILTER_EN;
361
362 /* DC offset to 0. */
363 TV_OUT(TV_LEVEL, 0x00000000);
364 TV_OUT(TV_OFFSET, 0x008080f0);
365
366#ifdef CONFIG_FB_MSM_TVOUT_SVIDEO
367 reg |= TVENC_CTL_S_VIDEO_EN;
368#endif
369#if defined(CONFIG_FB_MSM_MDP31)
370 TV_OUT(TV_DAC_INTF, 0x29);
371#endif
372 TV_OUT(TV_ENC_CTL, reg);
373
374 if (!tvout_msm_state->hpd_initialized) {
375 tvout_msm_state->hpd_initialized = TRUE;
376 /* Load detect enable */
377 reg = TV_IN(TV_DAC_INTF);
378 reg |= TVENC_LOAD_DETECT_EN;
379 TV_OUT(TV_DAC_INTF, reg);
380 }
381
382 tvout_msm_state->disp_powered_up = TRUE;
383 tvout_msm_turn_on(TRUE);
384
385 if (tvenc_pdata->poll) {
386 /* Enable Load present & removal interrupts for Video1 */
387 TV_OUT(TV_INTR_ENABLE, 0x5);
388
389 /* Enable interrupts when display is on */
390 enable_irq(tvout_msm_state->irq);
391 }
392 return 0;
393}
394
395static int tvout_off(struct platform_device *pdev)
396{
397 /* Disable TV encoder irqs when display is off */
398 if (tvenc_pdata->poll)
399 disable_irq(tvout_msm_state->irq);
400 tvout_msm_turn_on(FALSE);
401 tvout_msm_state->hpd_initialized = FALSE;
402 tvout_msm_state->disp_powered_up = FALSE;
403 if (tvenc_pdata->poll) {
404 mod_timer(&tvout_msm_state->hpd_work_timer, jiffies
405 + msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE));
406 }
407 return 0;
408}
409
410static int __devinit tvout_probe(struct platform_device *pdev)
411{
412 int rc = 0;
413 uint32 reg;
414 struct platform_device *fb_dev;
415
416#ifdef CONFIG_FB_MSM_TVOUT_NTSC_M
417 external_common_state->video_resolution = NTSC_M;
418#elif defined CONFIG_FB_MSM_TVOUT_NTSC_J
419 external_common_state->video_resolution = NTSC_J;
420#elif defined CONFIG_FB_MSM_TVOUT_PAL_M
421 external_common_state->video_resolution = PAL_M;
422#elif defined CONFIG_FB_MSM_TVOUT_PAL_N
423 external_common_state->video_resolution = PAL_N;
424#elif defined CONFIG_FB_MSM_TVOUT_PAL_BDGHIN
425 external_common_state->video_resolution = PAL_BDGHIN;
426#endif
427 external_common_state->dev = &pdev->dev;
428 if (pdev->id == 0) {
429 struct resource *res;
430
431 #define GET_RES(name, mode) do { \
432 res = platform_get_resource_byname(pdev, mode, name); \
433 if (!res) { \
434 DEV_DBG("'" name "' resource not found\n"); \
435 rc = -ENODEV; \
436 goto error; \
437 } \
438 } while (0)
439
440 #define GET_IRQ(var, name) do { \
441 GET_RES(name, IORESOURCE_IRQ); \
442 var = res->start; \
443 } while (0)
444
445 GET_IRQ(tvout_msm_state->irq, "tvout_device_irq");
446 #undef GET_IRQ
447 #undef GET_RES
448 return 0;
449 }
450
451 DEV_DBG("%s: tvout_msm_state->irq : %d",
452 __func__, tvout_msm_state->irq);
453
454 rc = request_irq(tvout_msm_state->irq, &tvout_msm_isr,
455 IRQF_TRIGGER_HIGH, "tvout_msm_isr", NULL);
456
457 if (rc) {
458 DEV_DBG("Init FAILED: IRQ request, rc=%d\n", rc);
459 goto error;
460 }
461 disable_irq(tvout_msm_state->irq);
462
463 init_timer(&tvout_msm_state->hpd_state_timer);
464 tvout_msm_state->hpd_state_timer.function =
465 tvout_msm_hpd_state_timer;
466 tvout_msm_state->hpd_state_timer.data = (uint32)NULL;
467 tvout_msm_state->hpd_state_timer.expires = jiffies
468 + msecs_to_jiffies(1000);
469
470 if (tvenc_pdata->poll) {
471 init_timer(&tvout_msm_state->hpd_work_timer);
472 tvout_msm_state->hpd_work_timer.function =
473 tvout_msm_hpd_work_timer;
474 tvout_msm_state->hpd_work_timer.data = (uint32)NULL;
475 tvout_msm_state->hpd_work_timer.expires = jiffies
476 + msecs_to_jiffies(1000);
477 }
478 fb_dev = msm_fb_add_device(pdev);
479 if (fb_dev) {
480 rc = external_common_state_create(fb_dev);
481 if (rc) {
482 DEV_ERR("Init FAILED: tvout_msm_state_create, rc=%d\n",
483 rc);
484 goto error;
485 }
486 if (tvenc_pdata->poll) {
487 /* Start polling timer to detect load */
488 mod_timer(&tvout_msm_state->hpd_work_timer, jiffies
489 + msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE));
490 } else {
491 /* Enable interrupt to detect load */
492 tvenc_set_encoder_clock(CLOCK_ON);
493 reg = TV_IN(TV_DAC_INTF);
494 reg |= TVENC_LOAD_DETECT_EN;
495 TV_OUT(TV_DAC_INTF, reg);
496 TV_OUT(TV_INTR_ENABLE, 0x5);
497 enable_irq(tvout_msm_state->irq);
498 }
499 } else
500 DEV_ERR("Init FAILED: failed to add fb device\n");
501error:
502 return 0;
503}
504
505static int __devexit tvout_remove(struct platform_device *pdev)
506{
507 external_common_state_remove();
508 kfree(tvout_msm_state);
509 tvout_msm_state = NULL;
510 return 0;
511}
512
513#ifdef CONFIG_SUSPEND
514static int tvout_device_pm_suspend(struct device *dev)
515{
516 mutex_lock(&tvout_msm_state_mutex);
517 if (tvout_msm_state->pm_suspended) {
518 mutex_unlock(&tvout_msm_state_mutex);
519 return 0;
520 }
521 if (tvenc_pdata->poll) {
522 if (del_timer(&tvout_msm_state->hpd_work_timer))
523 DEV_DBG("%s: suspending cable detect timer\n",
524 __func__);
525 } else {
526 disable_irq(tvout_msm_state->irq);
527 tvenc_set_encoder_clock(CLOCK_OFF);
528 }
529 tvout_msm_state->pm_suspended = TRUE;
530 mutex_unlock(&tvout_msm_state_mutex);
531 return 0;
532}
533
534static int tvout_device_pm_resume(struct device *dev)
535{
536 mutex_lock(&tvout_msm_state_mutex);
537 if (!tvout_msm_state->pm_suspended) {
538 mutex_unlock(&tvout_msm_state_mutex);
539 return 0;
540 }
541
542 if (tvenc_pdata->poll) {
543 tvout_msm_state->pm_suspended = FALSE;
544 mod_timer(&tvout_msm_state->hpd_work_timer, jiffies
545 + msecs_to_jiffies(TVOUT_HPD_DUTY_CYCLE));
546 mutex_unlock(&tvout_msm_state_mutex);
547 DEV_DBG("%s: resuming cable detect timer\n", __func__);
548 } else {
549 tvenc_set_encoder_clock(CLOCK_ON);
550 tvout_msm_state->pm_suspended = FALSE;
551 mutex_unlock(&tvout_msm_state_mutex);
552 enable_irq(tvout_msm_state->irq);
553 DEV_DBG("%s: enable cable detect interrupt\n", __func__);
554 }
555 return 0;
556}
557#else
558#define tvout_device_pm_suspend NULL
559#define tvout_device_pm_resume NULL
560#endif
561
562
563static const struct dev_pm_ops tvout_device_pm_ops = {
564 .suspend = tvout_device_pm_suspend,
565 .resume = tvout_device_pm_resume,
566};
567
568static struct platform_driver this_driver = {
569 .probe = tvout_probe,
570 .remove = tvout_remove,
571 .driver = {
572 .name = "tvout_device",
573 .pm = &tvout_device_pm_ops,
574 },
575};
576
577static struct msm_fb_panel_data tvout_panel_data = {
578 .panel_info.xres = TV_DIMENSION_MAX_WIDTH,
579 .panel_info.yres = TV_DIMENSION_MAX_HEIGHT,
580 .panel_info.type = TV_PANEL,
581 .panel_info.pdest = DISPLAY_2,
582 .panel_info.wait_cycle = 0,
583#ifdef CONFIG_FB_MSM_MDP40
584 .panel_info.bpp = 24,
585#else
586 .panel_info.bpp = 16,
587#endif
588 .panel_info.fb_num = 2,
589 .on = tvout_on,
590 .off = tvout_off,
591};
592
593static struct platform_device this_device = {
594 .name = "tvout_device",
595 .id = 1,
596 .dev = {
597 .platform_data = &tvout_panel_data,
598 }
599};
600
601static int __init tvout_init(void)
602{
603 int ret;
Ravishangar Kalyanamc719c542011-07-28 16:49:25 -0700604
605 if (msm_fb_detect_client("tvout_msm"))
606 return 0;
607
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 tvout_msm_state = kzalloc(sizeof(*tvout_msm_state), GFP_KERNEL);
609 if (!tvout_msm_state) {
610 DEV_ERR("tvout_msm_init FAILED: out of memory\n");
611 ret = -ENOMEM;
612 goto init_exit;
613 }
614
615 external_common_state = &tvout_msm_state->common;
616 ret = platform_driver_register(&this_driver);
617 if (ret) {
618 DEV_ERR("tvout_device_init FAILED: platform_driver_register\
619 rc=%d\n", ret);
620 goto init_exit;
621 }
622
623 ret = platform_device_register(&this_device);
624 if (ret) {
625 DEV_ERR("tvout_device_init FAILED: platform_driver_register\
626 rc=%d\n", ret);
627 platform_driver_unregister(&this_driver);
628 goto init_exit;
629 }
630
631 INIT_WORK(&tvout_msm_state->hpd_work, tvout_msm_hpd_work);
632 return 0;
633
634init_exit:
635 kfree(tvout_msm_state);
636 tvout_msm_state = NULL;
637 return ret;
638}
639
640static void __exit tvout_exit(void)
641{
642 platform_device_unregister(&this_device);
643 platform_driver_unregister(&this_driver);
644}
645
646module_init(tvout_init);
647module_exit(tvout_exit);
648
649MODULE_LICENSE("GPL v2");
650MODULE_VERSION("1.0");
651MODULE_AUTHOR("Qualcomm Innovation Center, Inc.");
652MODULE_DESCRIPTION("TV out driver");