blob: 0ecd593c43a3372fd60abc31f5cd134f41a3e747 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2008-2009, 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 <asm/io.h>
24
25#include <asm/system.h>
26#include <asm/mach-types.h>
27#include <linux/semaphore.h>
28#include <linux/uaccess.h>
29#include <linux/clk.h>
30#include <mach/clk.h>
31#include <linux/platform_device.h>
32#include <linux/pm_runtime.h>
33
34#include "msm_fb.h"
35#include "mddihosti.h"
36
37static int mddi_ext_probe(struct platform_device *pdev);
38static int mddi_ext_remove(struct platform_device *pdev);
39
40static int mddi_ext_off(struct platform_device *pdev);
41static int mddi_ext_on(struct platform_device *pdev);
42
43static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
44static int pdev_list_cnt;
45
46static int mddi_ext_suspend(struct platform_device *pdev, pm_message_t state);
47static int mddi_ext_resume(struct platform_device *pdev);
48
49#ifdef CONFIG_HAS_EARLYSUSPEND
50static void mddi_ext_early_suspend(struct early_suspend *h);
51static void mddi_ext_early_resume(struct early_suspend *h);
52#endif
53
54static int mddi_ext_runtime_suspend(struct device *dev)
55{
56 dev_dbg(dev, "pm_runtime: suspending...\n");
57 return 0;
58}
59
60static int mddi_ext_runtime_resume(struct device *dev)
61{
62 dev_dbg(dev, "pm_runtime: resuming...\n");
63 return 0;
64}
65
66static int mddi_ext_runtime_idle(struct device *dev)
67{
68 dev_dbg(dev, "pm_runtime: idling...\n");
69 return 0;
70}
71static struct dev_pm_ops mddi_ext_dev_pm_ops = {
72 .runtime_suspend = mddi_ext_runtime_suspend,
73 .runtime_resume = mddi_ext_runtime_resume,
74 .runtime_idle = mddi_ext_runtime_idle,
75};
76
77static struct platform_driver mddi_ext_driver = {
78 .probe = mddi_ext_probe,
79 .remove = mddi_ext_remove,
80#ifndef CONFIG_HAS_EARLYSUSPEND
81#ifdef CONFIG_PM
82 .suspend = mddi_ext_suspend,
83 .resume = mddi_ext_resume,
84#endif
85#endif
86 .resume_early = NULL,
87 .resume = NULL,
88 .shutdown = NULL,
89 .driver = {
90 .name = "mddi_ext",
91 .pm = &mddi_ext_dev_pm_ops,
92 },
93};
94
95static struct clk *mddi_ext_clk;
96static struct clk *mddi_ext_pclk;
97static struct mddi_platform_data *mddi_ext_pdata;
98
99extern int int_mddi_ext_flag;
100
101static int mddi_ext_off(struct platform_device *pdev)
102{
103 int ret = 0;
104
105 ret = panel_next_off(pdev);
106 mddi_host_stop_ext_display();
107 pm_runtime_put(&pdev->dev);
108 return ret;
109}
110
111static int mddi_ext_on(struct platform_device *pdev)
112{
113 int ret = 0;
114 u32 clk_rate;
115 struct msm_fb_data_type *mfd;
116
117 mfd = platform_get_drvdata(pdev);
118 pm_runtime_get(&pdev->dev);
119 clk_rate = mfd->fbi->var.pixclock;
120 clk_rate = min(clk_rate, mfd->panel_info.clk_max);
121
122 if (mddi_ext_pdata &&
123 mddi_ext_pdata->mddi_sel_clk &&
124 mddi_ext_pdata->mddi_sel_clk(&clk_rate))
125 printk(KERN_ERR
126 "%s: can't select mddi io clk targate rate = %d\n",
127 __func__, clk_rate);
128
129 if (clk_set_min_rate(mddi_ext_clk, clk_rate) < 0)
130 printk(KERN_ERR "%s: clk_set_min_rate failed\n",
131 __func__);
132
133 mddi_host_start_ext_display();
134 ret = panel_next_on(pdev);
135
136 return ret;
137}
138
139static int mddi_ext_resource_initialized;
140
141static int mddi_ext_probe(struct platform_device *pdev)
142{
143 struct msm_fb_data_type *mfd;
144 struct platform_device *mdp_dev = NULL;
145 struct msm_fb_panel_data *pdata = NULL;
146 int rc;
147 resource_size_t size ;
148 u32 clk_rate;
149
150 if ((pdev->id == 0) && (pdev->num_resources >= 0)) {
151 mddi_ext_pdata = pdev->dev.platform_data;
152
153 size = resource_size(&pdev->resource[0]);
154 msm_emdh_base = ioremap(pdev->resource[0].start, size);
155
156 MSM_FB_INFO("external mddi base address = 0x%x\n",
157 pdev->resource[0].start);
158
159 if (unlikely(!msm_emdh_base))
160 return -ENOMEM;
161
162 mddi_ext_resource_initialized = 1;
163 return 0;
164 }
165
166 if (!mddi_ext_resource_initialized)
167 return -EPERM;
168
169 mfd = platform_get_drvdata(pdev);
170
171 if (!mfd)
172 return -ENODEV;
173
174 if (mfd->key != MFD_KEY)
175 return -EINVAL;
176
177 if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
178 return -ENOMEM;
179
180 mdp_dev = platform_device_alloc("mdp", pdev->id);
181 if (!mdp_dev)
182 return -ENOMEM;
183
184 /*
185 * link to the latest pdev
186 */
187 mfd->pdev = mdp_dev;
188 mfd->dest = DISPLAY_EXT_MDDI;
189
190 /*
191 * alloc panel device data
192 */
193 if (platform_device_add_data
194 (mdp_dev, pdev->dev.platform_data,
195 sizeof(struct msm_fb_panel_data))) {
196 printk(KERN_ERR "mddi_ext_probe: platform_device_add_data failed!\n");
197 platform_device_put(mdp_dev);
198 return -ENOMEM;
199 }
200 /*
201 * data chain
202 */
203 pdata = mdp_dev->dev.platform_data;
204 pdata->on = mddi_ext_on;
205 pdata->off = mddi_ext_off;
206 pdata->next = pdev;
207
208 /*
209 * get/set panel specific fb info
210 */
211 mfd->panel_info = pdata->panel_info;
212 mfd->fb_imgType = MDP_RGB_565;
213
214 clk_rate = mfd->panel_info.clk_max;
215 if (mddi_ext_pdata &&
216 mddi_ext_pdata->mddi_sel_clk &&
217 mddi_ext_pdata->mddi_sel_clk(&clk_rate))
218 printk(KERN_ERR
219 "%s: can't select mddi io clk targate rate = %d\n",
220 __func__, clk_rate);
221
222 if (clk_set_max_rate(mddi_ext_clk, clk_rate) < 0)
223 printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__);
224 mfd->panel_info.clk_rate = mfd->panel_info.clk_min;
225
226 /*
227 * set driver data
228 */
229 platform_set_drvdata(mdp_dev, mfd);
230 rc = pm_runtime_set_active(&pdev->dev);
231 if (rc < 0)
232 printk(KERN_ERR "pm_runtime: fail to set active\n");
233
234 rc = 0;
235 pm_runtime_enable(&pdev->dev);
236 /*
237 * register in mdp driver
238 */
239 rc = platform_device_add(mdp_dev);
240 if (rc)
241 goto mddi_ext_probe_err;
242
243 pdev_list[pdev_list_cnt++] = pdev;
244
245#ifdef CONFIG_HAS_EARLYSUSPEND
246 mfd->mddi_ext_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
247 mfd->mddi_ext_early_suspend.suspend = mddi_ext_early_suspend;
248 mfd->mddi_ext_early_suspend.resume = mddi_ext_early_resume;
249 register_early_suspend(&mfd->mddi_ext_early_suspend);
250#endif
251
252 return 0;
253
254mddi_ext_probe_err:
255 platform_device_put(mdp_dev);
256 return rc;
257}
258
259static int mddi_ext_is_in_suspend;
260
261static int mddi_ext_suspend(struct platform_device *pdev, pm_message_t state)
262{
263 if (mddi_ext_is_in_suspend)
264 return 0;
265
266 mddi_ext_is_in_suspend = 1;
267
268 if (clk_set_min_rate(mddi_ext_clk, 0) < 0)
269 printk(KERN_ERR "%s: clk_set_min_rate failed\n", __func__);
270
271 clk_disable(mddi_ext_clk);
272 if (mddi_ext_pclk)
273 clk_disable(mddi_ext_pclk);
274
275 disable_irq(INT_MDDI_EXT);
276
277 return 0;
278}
279
280static int mddi_ext_resume(struct platform_device *pdev)
281{
282 struct msm_fb_data_type *mfd;
283
284 mfd = platform_get_drvdata(pdev);
285
286 if (!mddi_ext_is_in_suspend)
287 return 0;
288
289 mddi_ext_is_in_suspend = 0;
290 enable_irq(INT_MDDI_EXT);
291
292 clk_enable(mddi_ext_clk);
293 if (mddi_ext_pclk)
294 clk_enable(mddi_ext_pclk);
295
296 return 0;
297}
298
299#ifdef CONFIG_HAS_EARLYSUSPEND
300static void mddi_ext_early_suspend(struct early_suspend *h)
301{
302 pm_message_t state;
303 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
304 mddi_ext_early_suspend);
305
306 state.event = PM_EVENT_SUSPEND;
307 mddi_ext_suspend(mfd->pdev, state);
308}
309
310static void mddi_ext_early_resume(struct early_suspend *h)
311{
312 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
313 mddi_ext_early_suspend);
314 mddi_ext_resume(mfd->pdev);
315}
316#endif
317
318static int mddi_ext_remove(struct platform_device *pdev)
319{
320 pm_runtim_disable(&pdev->dev);
321 iounmap(msm_emdh_base);
322 return 0;
323}
324
325static int mddi_ext_register_driver(void)
326{
327 return platform_driver_register(&mddi_ext_driver);
328}
329
330static int __init mddi_ext_driver_init(void)
331{
332 int ret;
333
334 mddi_ext_clk = clk_get(NULL, "emdh_clk");
335 if (IS_ERR(mddi_ext_clk)) {
336 printk(KERN_ERR "can't find emdh_clk\n");
337 return PTR_ERR(mddi_ext_clk);
338 }
339 clk_enable(mddi_ext_clk);
340
341 mddi_ext_pclk = clk_get(NULL, "emdh_pclk");
342 if (IS_ERR(mddi_ext_pclk))
343 mddi_ext_pclk = NULL;
344 else
345 clk_enable(mddi_ext_pclk);
346
347 ret = mddi_ext_register_driver();
348 if (ret) {
349 clk_disable(mddi_ext_clk);
350 clk_put(mddi_ext_clk);
351 if (mddi_ext_pclk) {
352 clk_disable(mddi_ext_pclk);
353 clk_put(mddi_ext_pclk);
354 }
355 printk(KERN_ERR "mddi_ext_register_driver() failed!\n");
356 return ret;
357 }
358 mddi_init();
359
360 return ret;
361}
362
363module_init(mddi_ext_driver_init);