blob: 672b2a70d67a48f4f102bd4320943562dc390a72 [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/kernel.h>
16#include <linux/sched.h>
17#include <linux/time.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/spinlock.h>
21#include <linux/delay.h>
22#include <mach/hardware.h>
23#include <linux/io.h>
24#include <linux/pm_runtime.h>
25
26#include <asm/system.h>
27#include <asm/mach-types.h>
28#include <linux/semaphore.h>
29#include <linux/uaccess.h>
30#include <linux/clk.h>
31#include <linux/platform_device.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032
33#define TVENC_C
34#include "tvenc.h"
35#include "msm_fb.h"
36#include "mdp4.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037/* AXI rate in KHz */
38#define MSM_SYSTEM_BUS_RATE 128000000
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
40static int tvenc_probe(struct platform_device *pdev);
41static int tvenc_remove(struct platform_device *pdev);
42
43static int tvenc_off(struct platform_device *pdev);
44static int tvenc_on(struct platform_device *pdev);
45
46static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
47static int pdev_list_cnt;
48
49static struct clk *tvenc_clk;
50static struct clk *tvdac_clk;
51static struct clk *tvenc_pclk;
52static struct clk *mdp_tv_clk;
53#ifdef CONFIG_FB_MSM_MDP40
54static struct clk *tv_src_clk;
55#endif
56
57#ifdef CONFIG_MSM_BUS_SCALING
58static uint32_t tvenc_bus_scale_handle;
59#endif
60
61static int tvenc_runtime_suspend(struct device *dev)
62{
63 dev_dbg(dev, "pm_runtime: suspending...\n");
64 return 0;
65}
66
67static int tvenc_runtime_resume(struct device *dev)
68{
69 dev_dbg(dev, "pm_runtime: resuming...\n");
70 return 0;
71}
72
73static struct dev_pm_ops tvenc_dev_pm_ops = {
74 .runtime_suspend = tvenc_runtime_suspend,
75 .runtime_resume = tvenc_runtime_resume,
76};
77
78static struct platform_driver tvenc_driver = {
79 .probe = tvenc_probe,
80 .remove = tvenc_remove,
81 .suspend = NULL,
82 .resume = NULL,
83 .shutdown = NULL,
84 .driver = {
85 .name = "tvenc",
86 .pm = &tvenc_dev_pm_ops
87 },
88};
89
90int tvenc_set_encoder_clock(boolean clock_on)
91{
92 int ret = 0;
93 if (clock_on) {
94#ifdef CONFIG_FB_MSM_MDP40
95 /* Consolidated clock used by both HDMI & TV encoder.
96 Clock exists only in MDP4 and not in older versions */
97 ret = clk_set_rate(tv_src_clk, 27000000);
98 if (ret) {
99 pr_err("%s: tvsrc_clk set rate failed! %d\n",
100 __func__, ret);
101 goto tvsrc_err;
102 }
103#endif
104 ret = clk_enable(tvenc_clk);
105 if (ret) {
106 pr_err("%s: tvenc_clk enable failed! %d\n",
107 __func__, ret);
108 goto tvsrc_err;
109 }
110
111 if (!IS_ERR(tvenc_pclk)) {
112 ret = clk_enable(tvenc_pclk);
113 if (ret) {
114 pr_err("%s: tvenc_pclk enable failed! %d\n",
115 __func__, ret);
116 goto tvencp_err;
117 }
118 }
119 return ret;
120 } else {
121 if (!IS_ERR(tvenc_pclk))
122 clk_disable(tvenc_pclk);
123 clk_disable(tvenc_clk);
124 return ret;
125 }
126tvencp_err:
127 clk_disable(tvenc_clk);
128tvsrc_err:
129 return ret;
130}
131
132int tvenc_set_clock(boolean clock_on)
133{
134 int ret = 0;
135 if (clock_on) {
136 if (tvenc_pdata->poll) {
137 ret = tvenc_set_encoder_clock(CLOCK_ON);
138 if (ret) {
139 pr_err("%s: TVenc clock(s) enable failed! %d\n",
140 __func__, ret);
141 goto tvenc_err;
142 }
143 }
144 ret = clk_enable(tvdac_clk);
145 if (ret) {
146 pr_err("%s: tvdac_clk enable failed! %d\n",
147 __func__, ret);
148 goto tvdac_err;
149 }
150 if (!IS_ERR(mdp_tv_clk)) {
151 ret = clk_enable(mdp_tv_clk);
152 if (ret) {
153 pr_err("%s: mdp_tv_clk enable failed! %d\n",
154 __func__, ret);
155 goto mdptv_err;
156 }
157 }
158 return ret;
159 } else {
160 if (!IS_ERR(mdp_tv_clk))
161 clk_disable(mdp_tv_clk);
162 clk_disable(tvdac_clk);
163 if (tvenc_pdata->poll)
164 tvenc_set_encoder_clock(CLOCK_OFF);
165 return ret;
166 }
167
168mdptv_err:
169 clk_disable(tvdac_clk);
170tvdac_err:
171 tvenc_set_encoder_clock(CLOCK_OFF);
172tvenc_err:
173 return ret;
174}
175
176static int tvenc_off(struct platform_device *pdev)
177{
178 int ret = 0;
179
180 struct msm_fb_data_type *mfd;
181
182 mfd = platform_get_drvdata(pdev);
183
184 ret = panel_next_off(pdev);
185 if (ret)
186 pr_err("%s: tvout_off failed! %d\n",
187 __func__, ret);
188
189 tvenc_set_clock(CLOCK_OFF);
190
191 if (tvenc_pdata && tvenc_pdata->pm_vid_en)
192 ret = tvenc_pdata->pm_vid_en(0);
193#ifdef CONFIG_MSM_BUS_SCALING
194 if (tvenc_bus_scale_handle > 0)
195 msm_bus_scale_client_update_request(tvenc_bus_scale_handle,
196 0);
197#else
198 if (mfd->ebi1_clk)
199 clk_disable(mfd->ebi1_clk);
200#endif
201
202 if (ret)
203 pr_err("%s: pm_vid_en(off) failed! %d\n",
204 __func__, ret);
205 mdp4_extn_disp = 0;
206 return ret;
207}
208
209static int tvenc_on(struct platform_device *pdev)
210{
211 int ret = 0;
212
213#ifndef CONFIG_MSM_BUS_SCALING
214 struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
215#endif
216
217#ifdef CONFIG_MSM_BUS_SCALING
218 if (tvenc_bus_scale_handle > 0)
219 msm_bus_scale_client_update_request(tvenc_bus_scale_handle,
220 1);
221#else
222 if (mfd->ebi1_clk)
223 clk_enable(mfd->ebi1_clk);
224#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 mdp4_extn_disp = 1;
226 if (tvenc_pdata && tvenc_pdata->pm_vid_en)
227 ret = tvenc_pdata->pm_vid_en(1);
228 if (ret) {
229 pr_err("%s: pm_vid_en(on) failed! %d\n",
230 __func__, ret);
231 return ret;
232 }
233
234 ret = tvenc_set_clock(CLOCK_ON);
235 if (ret) {
236 pr_err("%s: tvenc_set_clock(CLOCK_ON) failed! %d\n",
237 __func__, ret);
238 tvenc_pdata->pm_vid_en(0);
239 goto error;
240 }
241
242 ret = panel_next_on(pdev);
243 if (ret) {
244 pr_err("%s: tvout_on failed! %d\n",
245 __func__, ret);
246 tvenc_set_clock(CLOCK_OFF);
247 tvenc_pdata->pm_vid_en(0);
248 }
249
250error:
251 return ret;
252
253}
254
255void tvenc_gen_test_pattern(struct msm_fb_data_type *mfd)
256{
257 uint32 reg = 0, i;
258
259 reg = readl(MSM_TV_ENC_CTL);
260 reg |= TVENC_CTL_TEST_PATT_EN;
261
262 for (i = 0; i < 3; i++) {
263 TV_OUT(TV_ENC_CTL, 0); /* disable TV encoder */
264
265 switch (i) {
266 /*
267 * TV Encoder - Color Bar Test Pattern
268 */
269 case 0:
270 reg |= TVENC_CTL_TPG_CLRBAR;
271 break;
272 /*
273 * TV Encoder - Red Frame Test Pattern
274 */
275 case 1:
276 reg |= TVENC_CTL_TPG_REDCLR;
277 break;
278 /*
279 * TV Encoder - Modulated Ramp Test Pattern
280 */
281 default:
282 reg |= TVENC_CTL_TPG_MODRAMP;
283 break;
284 }
285
286 TV_OUT(TV_ENC_CTL, reg);
287 mdelay(5000);
288
289 switch (i) {
290 /*
291 * TV Encoder - Color Bar Test Pattern
292 */
293 case 0:
294 reg &= ~TVENC_CTL_TPG_CLRBAR;
295 break;
296 /*
297 * TV Encoder - Red Frame Test Pattern
298 */
299 case 1:
300 reg &= ~TVENC_CTL_TPG_REDCLR;
301 break;
302 /*
303 * TV Encoder - Modulated Ramp Test Pattern
304 */
305 default:
306 reg &= ~TVENC_CTL_TPG_MODRAMP;
307 break;
308 }
309 }
310}
311
312static int tvenc_resource_initialized;
313
314static int tvenc_probe(struct platform_device *pdev)
315{
316 struct msm_fb_data_type *mfd;
317 struct platform_device *mdp_dev = NULL;
318 struct msm_fb_panel_data *pdata = NULL;
319 int rc;
320
321 if (pdev->id == 0) {
322 tvenc_base = ioremap(pdev->resource[0].start,
323 pdev->resource[0].end -
324 pdev->resource[0].start + 1);
325 if (!tvenc_base) {
326 pr_err("tvenc_base ioremap failed!\n");
327 return -ENOMEM;
328 }
329 tvenc_pdata = pdev->dev.platform_data;
330 tvenc_resource_initialized = 1;
331 return 0;
332 }
333
334 if (!tvenc_resource_initialized)
335 return -EPERM;
336
337 mfd = platform_get_drvdata(pdev);
338
339 if (!mfd)
340 return -ENODEV;
341
342 if (mfd->key != MFD_KEY)
343 return -EINVAL;
344
345 if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
346 return -ENOMEM;
347
348 if (tvenc_base == NULL)
349 return -ENOMEM;
350
351 mdp_dev = platform_device_alloc("mdp", pdev->id);
352 if (!mdp_dev)
353 return -ENOMEM;
354
355 /*
356 * link to the latest pdev
357 */
358 mfd->pdev = mdp_dev;
359 mfd->dest = DISPLAY_TV;
360
361 /*
362 * alloc panel device data
363 */
364 if (platform_device_add_data
365 (mdp_dev, pdev->dev.platform_data,
366 sizeof(struct msm_fb_panel_data))) {
367 pr_err("tvenc_probe: platform_device_add_data failed!\n");
368 platform_device_put(mdp_dev);
369 return -ENOMEM;
370 }
371 /*
372 * data chain
373 */
374 pdata = mdp_dev->dev.platform_data;
375 pdata->on = tvenc_on;
376 pdata->off = tvenc_off;
377 pdata->next = pdev;
378
379 /*
380 * get/set panel specific fb info
381 */
382 mfd->panel_info = pdata->panel_info;
383#ifdef CONFIG_FB_MSM_MDP40
384 mfd->fb_imgType = MDP_RGB_565; /* base layer */
385#else
386 mfd->fb_imgType = MDP_YCRYCB_H2V1;
387#endif
388
389#ifdef CONFIG_MSM_BUS_SCALING
390 if (!tvenc_bus_scale_handle && tvenc_pdata &&
391 tvenc_pdata->bus_scale_table) {
392 tvenc_bus_scale_handle =
393 msm_bus_scale_register_client(
394 tvenc_pdata->bus_scale_table);
395 if (!tvenc_bus_scale_handle) {
396 printk(KERN_ERR "%s not able to get bus scale\n",
397 __func__);
398 }
399 }
400#else
401 mfd->ebi1_clk = clk_get(NULL, "ebi1_tv_clk");
402 if (IS_ERR(mfd->ebi1_clk)) {
403 rc = PTR_ERR(mfd->ebi1_clk);
404 goto tvenc_probe_err;
405 }
406 clk_set_rate(mfd->ebi1_clk, MSM_SYSTEM_BUS_RATE);
407#endif
408
409 /*
410 * set driver data
411 */
412 platform_set_drvdata(mdp_dev, mfd);
413
414 /*
415 * register in mdp driver
416 */
417 rc = platform_device_add(mdp_dev);
418 if (rc)
419 goto tvenc_probe_err;
420
421 pm_runtime_set_active(&pdev->dev);
422 pm_runtime_enable(&pdev->dev);
423
424
425
426 pdev_list[pdev_list_cnt++] = pdev;
427
428 return 0;
429
430tvenc_probe_err:
431#ifdef CONFIG_MSM_BUS_SCALING
432 if (tvenc_pdata && tvenc_pdata->bus_scale_table &&
433 tvenc_bus_scale_handle > 0) {
434 msm_bus_scale_unregister_client(tvenc_bus_scale_handle);
435 tvenc_bus_scale_handle = 0;
436 }
437#endif
438 platform_device_put(mdp_dev);
439 return rc;
440}
441
442static int tvenc_remove(struct platform_device *pdev)
443{
444 struct msm_fb_data_type *mfd;
445
446 mfd = platform_get_drvdata(pdev);
447
448#ifdef CONFIG_MSM_BUS_SCALING
449 if (tvenc_pdata && tvenc_pdata->bus_scale_table &&
450 tvenc_bus_scale_handle > 0) {
451 msm_bus_scale_unregister_client(tvenc_bus_scale_handle);
452 tvenc_bus_scale_handle = 0;
453 }
454#else
455 clk_put(mfd->ebi1_clk);
456#endif
457
458 pm_runtime_disable(&pdev->dev);
459 return 0;
460}
461
462static int tvenc_register_driver(void)
463{
464 return platform_driver_register(&tvenc_driver);
465}
466
467static int __init tvenc_driver_init(void)
468{
469 int ret;
470 tvenc_clk = clk_get(NULL, "tv_enc_clk");
471 tvdac_clk = clk_get(NULL, "tv_dac_clk");
472 tvenc_pclk = clk_get(NULL, "tv_enc_pclk");
473 mdp_tv_clk = clk_get(NULL, "mdp_tv_clk");
474
475#ifdef CONFIG_FB_MSM_MDP40
476 tv_src_clk = clk_get(NULL, "tv_src_clk");
477 if (IS_ERR(tv_src_clk))
478 tv_src_clk = tvenc_clk; /* Fallback to slave */
479#endif
480
481 if (IS_ERR(tvenc_clk)) {
482 pr_err("%s: error: can't get tvenc_clk!\n", __func__);
483 return PTR_ERR(tvenc_clk);
484 }
485
486 if (IS_ERR(tvdac_clk)) {
487 pr_err("%s: error: can't get tvdac_clk!\n", __func__);
488 return PTR_ERR(tvdac_clk);
489 }
490
491 if (IS_ERR(tvenc_pclk)) {
492 ret = PTR_ERR(tvenc_pclk);
493 if (-ENOENT == ret)
494 pr_info("%s: tvenc_pclk does not exist!\n", __func__);
495 else {
496 pr_err("%s: error: can't get tvenc_pclk!\n", __func__);
497 return ret;
498 }
499 }
500
501 if (IS_ERR(mdp_tv_clk)) {
502 ret = PTR_ERR(mdp_tv_clk);
503 if (-ENOENT == ret)
504 pr_info("%s: mdp_tv_clk does not exist!\n", __func__);
505 else {
506 pr_err("%s: error: can't get mdp_tv_clk!\n", __func__);
507 return ret;
508 }
509 }
510
511 return tvenc_register_driver();
512}
513
514module_init(tvenc_driver_init);