1/* 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 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 15 * 02110-1301, USA. 16 */ 17 18#include <linux/module.h> 19#include <linux/kernel.h> 20#include <linux/sched.h> 21#include <linux/time.h> 22#include <linux/init.h> 23#include <linux/interrupt.h> 24#include <linux/spinlock.h> 25#include <linux/delay.h> 26#include <mach/hardware.h> 27#include <asm/io.h> 28 29#include <asm/system.h> 30#include <asm/mach-types.h> 31#include <linux/semaphore.h> 32#include <linux/uaccess.h> 33#include <linux/clk.h> 34#include <linux/platform_device.h> 35 36#include "msm_fb.h" 37#include "mddihosti.h" 38#include "mddihost.h" 39#include <mach/gpio.h> 40#include <mach/clk.h> 41 42static int mddi_probe(struct platform_device *pdev); 43static int mddi_remove(struct platform_device *pdev); 44 45static int mddi_off(struct platform_device *pdev); 46static int mddi_on(struct platform_device *pdev); 47 48static int mddi_suspend(struct platform_device *pdev, pm_message_t state); 49static int mddi_resume(struct platform_device *pdev); 50 51#ifdef CONFIG_HAS_EARLYSUSPEND 52static void mddi_early_suspend(struct early_suspend *h); 53static void mddi_early_resume(struct early_suspend *h); 54#endif 55 56static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; 57static int pdev_list_cnt; 58static struct clk *mddi_clk; 59static struct clk *mddi_pclk; 60static struct mddi_platform_data *mddi_pdata; 61 62static struct platform_driver mddi_driver = { 63 .probe = mddi_probe, 64 .remove = mddi_remove, 65#ifndef CONFIG_HAS_EARLYSUSPEND 66#ifdef CONFIG_PM 67 .suspend = mddi_suspend, 68 .resume = mddi_resume, 69#endif 70#endif 71 .suspend_late = NULL, 72 .resume_early = NULL, 73 .shutdown = NULL, 74 .driver = { 75 .name = "mddi", 76 }, 77}; 78 79extern int int_mddi_pri_flag; 80 81static int mddi_off(struct platform_device *pdev) 82{ 83 int ret = 0; 84 85 ret = panel_next_off(pdev); 86 87 if (mddi_pdata && mddi_pdata->mddi_power_save) 88 mddi_pdata->mddi_power_save(0); 89 90 return ret; 91} 92 93static int mddi_on(struct platform_device *pdev) 94{ 95 int ret = 0; 96 u32 clk_rate; 97 struct msm_fb_data_type *mfd; 98 99 mfd = platform_get_drvdata(pdev); 100 101 if (mddi_pdata && mddi_pdata->mddi_power_save) 102 mddi_pdata->mddi_power_save(1); 103 104 clk_rate = mfd->fbi->var.pixclock; 105 clk_rate = min(clk_rate, mfd->panel_info.clk_max); 106 107 if (mddi_pdata && 108 mddi_pdata->mddi_sel_clk && 109 mddi_pdata->mddi_sel_clk(&clk_rate)) 110 printk(KERN_ERR 111 "%s: can't select mddi io clk targate rate = %d\n", 112 __func__, clk_rate); 113 114 if (clk_set_min_rate(mddi_clk, clk_rate) < 0) 115 printk(KERN_ERR "%s: clk_set_min_rate failed\n", 116 __func__); 117 118 ret = panel_next_on(pdev); 119 120 return ret; 121} 122 123static int mddi_resource_initialized; 124 125static int mddi_probe(struct platform_device *pdev) 126{ 127 struct msm_fb_data_type *mfd; 128 struct platform_device *mdp_dev = NULL; 129 struct msm_fb_panel_data *pdata = NULL; 130 int rc; 131 resource_size_t size ; 132 u32 clk_rate; 133 134 if ((pdev->id == 0) && (pdev->num_resources >= 0)) { 135 mddi_pdata = pdev->dev.platform_data; 136 137 size = resource_size(&pdev->resource[0]); 138 msm_pmdh_base = ioremap(pdev->resource[0].start, size); 139 140 MSM_FB_INFO("primary mddi base phy_addr = 0x%x virt = 0x%x\n", 141 pdev->resource[0].start, (int) msm_pmdh_base); 142 143 if (unlikely(!msm_pmdh_base)) 144 return -ENOMEM; 145 146 if (mddi_pdata && mddi_pdata->mddi_power_save) 147 mddi_pdata->mddi_power_save(1); 148 149 mddi_resource_initialized = 1; 150 return 0; 151 } 152 153 if (!mddi_resource_initialized) 154 return -EPERM; 155 156 mfd = platform_get_drvdata(pdev); 157 158 if (!mfd) 159 return -ENODEV; 160 161 if (mfd->key != MFD_KEY) 162 return -EINVAL; 163 164 if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) 165 return -ENOMEM; 166 167 mdp_dev = platform_device_alloc("mdp", pdev->id); 168 if (!mdp_dev) 169 return -ENOMEM; 170 171 /* 172 * link to the latest pdev 173 */ 174 mfd->pdev = mdp_dev; 175 mfd->dest = DISPLAY_LCD; 176 177 /* 178 * alloc panel device data 179 */ 180 if (platform_device_add_data 181 (mdp_dev, pdev->dev.platform_data, 182 sizeof(struct msm_fb_panel_data))) { 183 printk(KERN_ERR "mddi_probe: platform_device_add_data failed!\n"); 184 platform_device_put(mdp_dev); 185 return -ENOMEM; 186 } 187 /* 188 * data chain 189 */ 190 pdata = mdp_dev->dev.platform_data; 191 pdata->on = mddi_on; 192 pdata->off = mddi_off; 193 pdata->next = pdev; 194 195 /* 196 * get/set panel specific fb info 197 */ 198 mfd->panel_info = pdata->panel_info; 199 mfd->fb_imgType = MDP_RGB_565; 200 201 clk_rate = mfd->panel_info.clk_max; 202 if (mddi_pdata && 203 mddi_pdata->mddi_sel_clk && 204 mddi_pdata->mddi_sel_clk(&clk_rate)) 205 printk(KERN_ERR 206 "%s: can't select mddi io clk targate rate = %d\n", 207 __func__, clk_rate); 208 209 if (clk_set_max_rate(mddi_clk, clk_rate) < 0) 210 printk(KERN_ERR "%s: clk_set_max_rate failed\n", __func__); 211 mfd->panel_info.clk_rate = mfd->panel_info.clk_min; 212 213 /* 214 * set driver data 215 */ 216 platform_set_drvdata(mdp_dev, mfd); 217 218 /* 219 * register in mdp driver 220 */ 221 rc = platform_device_add(mdp_dev); 222 if (rc) 223 goto mddi_probe_err; 224 225 pdev_list[pdev_list_cnt++] = pdev; 226 227#ifdef CONFIG_HAS_EARLYSUSPEND 228 mfd->mddi_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB; 229 mfd->mddi_early_suspend.suspend = mddi_early_suspend; 230 mfd->mddi_early_suspend.resume = mddi_early_resume; 231 register_early_suspend(&mfd->mddi_early_suspend); 232#endif 233 234 return 0; 235 236mddi_probe_err: 237 platform_device_put(mdp_dev); 238 return rc; 239} 240 241static int mddi_pad_ctrl; 242static int mddi_power_locked; 243static int mddi_is_in_suspend; 244 245void mddi_disable(int lock) 246{ 247 mddi_host_type host_idx = MDDI_HOST_PRIM; 248 249 if (mddi_power_locked) 250 return; 251 252 if (lock) 253 mddi_power_locked = 1; 254 255 if (mddi_host_timer.function) 256 del_timer_sync(&mddi_host_timer); 257 258 mddi_pad_ctrl = mddi_host_reg_in(PAD_CTL); 259 mddi_host_reg_out(PAD_CTL, 0x0); 260 261 if (clk_set_min_rate(mddi_clk, 0) < 0) 262 printk(KERN_ERR "%s: clk_set_min_rate failed\n", __func__); 263 264 clk_disable(mddi_clk); 265 if (mddi_pclk) 266 clk_disable(mddi_pclk); 267 disable_irq(INT_MDDI_PRI); 268 269 if (mddi_pdata && mddi_pdata->mddi_power_save) 270 mddi_pdata->mddi_power_save(0); 271} 272 273static int mddi_suspend(struct platform_device *pdev, pm_message_t state) 274{ 275 if (mddi_is_in_suspend) 276 return 0; 277 278 mddi_is_in_suspend = 1; 279 mddi_disable(0); 280 return 0; 281} 282 283static int mddi_resume(struct platform_device *pdev) 284{ 285 mddi_host_type host_idx = MDDI_HOST_PRIM; 286 287 if (!mddi_is_in_suspend) 288 return 0; 289 290 mddi_is_in_suspend = 0; 291 292 if (mddi_power_locked) 293 return 0; 294 295 enable_irq(INT_MDDI_PRI); 296 clk_enable(mddi_clk); 297 if (mddi_pclk) 298 clk_enable(mddi_pclk); 299 mddi_host_reg_out(PAD_CTL, mddi_pad_ctrl); 300 301 if (mddi_host_timer.function) 302 mddi_host_timer_service(0); 303 304 return 0; 305} 306 307#ifdef CONFIG_HAS_EARLYSUSPEND 308static void mddi_early_suspend(struct early_suspend *h) 309{ 310 pm_message_t state; 311 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, 312 mddi_early_suspend); 313 314 state.event = PM_EVENT_SUSPEND; 315 mddi_suspend(mfd->pdev, state); 316} 317 318static void mddi_early_resume(struct early_suspend *h) 319{ 320 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type, 321 mddi_early_suspend); 322 mddi_resume(mfd->pdev); 323} 324#endif 325 326static int mddi_remove(struct platform_device *pdev) 327{ 328 if (mddi_host_timer.function) 329 del_timer_sync(&mddi_host_timer); 330 331 iounmap(msm_pmdh_base); 332 333 return 0; 334} 335 336static int mddi_register_driver(void) 337{ 338 return platform_driver_register(&mddi_driver); 339} 340 341static int __init mddi_driver_init(void) 342{ 343 int ret; 344 345 mddi_clk = clk_get(NULL, "mddi_clk"); 346 if (IS_ERR(mddi_clk)) { 347 printk(KERN_ERR "can't find mddi_clk \n"); 348 return PTR_ERR(mddi_clk); 349 } 350 clk_enable(mddi_clk); 351 352 mddi_pclk = clk_get(NULL, "mddi_pclk"); 353 if (IS_ERR(mddi_pclk)) 354 mddi_pclk = NULL; 355 else 356 clk_enable(mddi_pclk); 357 358 ret = mddi_register_driver(); 359 if (ret) { 360 clk_disable(mddi_clk); 361 clk_put(mddi_clk); 362 if (mddi_pclk) { 363 clk_disable(mddi_pclk); 364 clk_put(mddi_pclk); 365 } 366 printk(KERN_ERR "mddi_register_driver() failed!\n"); 367 return ret; 368 } 369 370 mddi_init(); 371 372 return ret; 373} 374 375module_init(mddi_driver_init); 376