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