OLD | NEW |
(Empty) | |
| 1 /* linux/drivers/media/video/samsung/tv20/s5p_tv_base.c |
| 2 * |
| 3 * Copyright (c) 2010 Samsung Electronics Co., Ltd. |
| 4 * http://www.samsung.com/ |
| 5 * |
| 6 * S5PV210 - Entry file for Samsung TVOut driver |
| 7 * |
| 8 * This program is free software; you can redistribute it and/or modify |
| 9 * it under the terms of the GNU General Public License version 2 as |
| 10 * published by the Free Software Foundation. |
| 11 */ |
| 12 |
| 13 #include <linux/init.h> |
| 14 #include <linux/module.h> |
| 15 #include <linux/kernel.h> |
| 16 #include <linux/string.h> |
| 17 #include <linux/fs.h> |
| 18 #include <linux/init.h> |
| 19 #include <linux/mm.h> |
| 20 #include <linux/interrupt.h> |
| 21 #include <linux/miscdevice.h> |
| 22 #include <linux/platform_device.h> |
| 23 #include <linux/workqueue.h> |
| 24 #include <linux/wait.h> |
| 25 #include <linux/ioctl.h> |
| 26 #include <linux/device.h> |
| 27 #include <linux/clk.h> |
| 28 #include <linux/i2c.h> |
| 29 #include <linux/delay.h> |
| 30 #include <linux/irq.h> |
| 31 |
| 32 #include <media/v4l2-common.h> |
| 33 #include <media/v4l2-ioctl.h> |
| 34 #include <mach/map.h> |
| 35 |
| 36 #include <mach/gpio.h> |
| 37 #include <plat/gpio-cfg.h> |
| 38 |
| 39 #include <linux/io.h> |
| 40 #include <linux/uaccess.h> |
| 41 |
| 42 #ifdef CONFIG_S5PV210_PM |
| 43 #include <mach/pd.h> |
| 44 #endif |
| 45 |
| 46 #include "s5p_tv.h" |
| 47 |
| 48 #ifdef CONFIG_TVOUT_DBG |
| 49 #define S5P_TV_BASE_DEBUG 1 |
| 50 #endif |
| 51 |
| 52 #ifdef S5P_TV_BASE_DEBUG |
| 53 #define BASEPRINTK(fmt, args...) \ |
| 54 printk(KERN_INFO "[TVBASE] %s: " fmt, __func__ , ## args) |
| 55 #else |
| 56 #define BASEPRINTK(fmt, args...) |
| 57 #endif |
| 58 |
| 59 |
| 60 #define TVOUT_CLK_INIT(dev, clk, name) |
| 61 |
| 62 #define TVOUT_IRQ_INIT(x, ret, dev, num, jump, ftn, m_name) \ |
| 63 do { \ |
| 64 x = platform_get_irq(dev, num); \ |
| 65 if (x < 0) { \ |
| 66 printk(KERN_ERR \ |
| 67 "failed to get %s irq resource\n", m_name); \ |
| 68 ret = -ENOENT; \ |
| 69 goto jump; \ |
| 70 } \ |
| 71 ret = request_irq(x, ftn, IRQF_DISABLED, dev->name, dev); \ |
| 72 if (ret != 0) { \ |
| 73 printk(KERN_ERR \ |
| 74 "failed to install %s irq (%d)\n", m_name, ret); \ |
| 75 goto jump; \ |
| 76 } \ |
| 77 } while (0) |
| 78 |
| 79 |
| 80 static struct mutex *mutex_for_fo; |
| 81 |
| 82 |
| 83 struct s5p_tv_status s5ptv_status; |
| 84 struct s5p_tv_vo s5ptv_overlay[2]; |
| 85 wait_queue_head_t s5ptv_wq; |
| 86 |
| 87 #ifdef I2C_BASE |
| 88 static struct mutex *mutex_for_i2c; |
| 89 static struct work_struct ws_hpd; |
| 90 spinlock_t slock_hpd; |
| 91 |
| 92 static struct i2c_driver hdcp_i2c_driver; |
| 93 static bool hdcp_i2c_drv_state; |
| 94 |
| 95 const static u16 ignore[] = {I2C_CLIENT_END}; |
| 96 const static u16 normal_addr[] = {(S5P_HDCP_I2C_ADDR >> 1), I2C_CLIENT_END}; |
| 97 const static u16 *forces[] = {NULL}; |
| 98 |
| 99 static struct i2c_client_address_data addr_data = { |
| 100 .normal_i2c = normal_addr, |
| 101 .probe = ignore, |
| 102 .ignore = ignore, |
| 103 .forces = forces, |
| 104 }; |
| 105 |
| 106 /* |
| 107 * i2c client drv. - register client drv |
| 108 */ |
| 109 static int hdcp_i2c_attach(struct i2c_adapter *adap, int addr, int kind) |
| 110 { |
| 111 |
| 112 struct i2c_client *c; |
| 113 |
| 114 c = kzalloc(sizeof(*c), GFP_KERNEL); |
| 115 |
| 116 if (!c) |
| 117 return -ENOMEM; |
| 118 |
| 119 strcpy(c->name, "s5p_ddc_client"); |
| 120 |
| 121 c->addr = addr; |
| 122 |
| 123 c->adapter = adap; |
| 124 |
| 125 c->driver = &hdcp_i2c_driver; |
| 126 |
| 127 s5ptv_status.hdcp_i2c_client = c; |
| 128 |
| 129 dev_info(&adap->dev, "s5p_ddc_client attached " |
| 130 "into s5p_ddc_port successfully\n"); |
| 131 |
| 132 return i2c_attach_client(c); |
| 133 } |
| 134 |
| 135 static int hdcp_i2c_attach_adapter(struct i2c_adapter *adap) |
| 136 { |
| 137 int ret = 0; |
| 138 |
| 139 ret = i2c_probe(adap, &addr_data, hdcp_i2c_attach); |
| 140 |
| 141 if (ret) { |
| 142 dev_err(&adap->dev, |
| 143 "failed to attach s5p_hdcp_port driver\n"); |
| 144 ret = -ENODEV; |
| 145 } |
| 146 |
| 147 return ret; |
| 148 } |
| 149 |
| 150 static int hdcp_i2c_detach(struct i2c_client *client) |
| 151 { |
| 152 dev_info(&client->adapter->dev, "s5p_ddc_client detached " |
| 153 "from s5p_ddc_port successfully\n"); |
| 154 |
| 155 i2c_detach_client(client); |
| 156 |
| 157 return 0; |
| 158 } |
| 159 |
| 160 static struct i2c_driver hdcp_i2c_driver = { |
| 161 .driver = { |
| 162 .name = "s5p_ddc_port", |
| 163 }, |
| 164 .id = I2C_DRIVERID_S5P_HDCP, |
| 165 .attach_adapter = hdcp_i2c_attach_adapter, |
| 166 .detach_client = hdcp_i2c_detach, |
| 167 }; |
| 168 |
| 169 static void set_ddc_port(void) |
| 170 { |
| 171 mutex_lock(mutex_for_i2c); |
| 172 |
| 173 if (s5ptv_status.hpd_status) { |
| 174 |
| 175 if (!hdcp_i2c_drv_state) |
| 176 /* cable : plugged, drv : unregistered */ |
| 177 if (i2c_add_driver(&hdcp_i2c_driver)) |
| 178 printk(KERN_INFO "HDCP port add failed\n"); |
| 179 |
| 180 /* changed drv. status */ |
| 181 hdcp_i2c_drv_state = true; |
| 182 |
| 183 |
| 184 /* cable inserted -> removed */ |
| 185 tv_set_hpd_detection(true, s5ptv_status.hdcp_en, |
| 186 s5ptv_status.hdcp_i2c_client); |
| 187 |
| 188 } else { |
| 189 |
| 190 if (hdcp_i2c_drv_state) |
| 191 /* cable : unplugged, drv : registered */ |
| 192 i2c_del_driver(&hdcp_i2c_driver); |
| 193 |
| 194 /* changed drv. status */ |
| 195 hdcp_i2c_drv_state = false; |
| 196 |
| 197 /* cable removed -> inserted */ |
| 198 tv_set_hpd_detection(false, s5ptv_status.hdcp_en, |
| 199 s5ptv_status.hdcp_i2c_client); |
| 200 } |
| 201 |
| 202 mutex_unlock(mutex_for_i2c); |
| 203 } |
| 204 #endif |
| 205 |
| 206 int tv_phy_power(bool on) |
| 207 { |
| 208 if (on) { |
| 209 /* on */ |
| 210 clk_enable(s5ptv_status.i2c_phy_clk); |
| 211 |
| 212 tv_hdmi_phy_power(true); |
| 213 |
| 214 } else { |
| 215 /* |
| 216 * for preventing hdmi hang up when restart |
| 217 * switch to internal clk - SCLK_DAC, SCLK_PIXEL |
| 218 */ |
| 219 tv_clk_change_internal(); |
| 220 |
| 221 tv_hdmi_phy_power(false); |
| 222 |
| 223 clk_disable(s5ptv_status.i2c_phy_clk); |
| 224 } |
| 225 |
| 226 return 0; |
| 227 } |
| 228 |
| 229 |
| 230 int s5p_tv_clk_gate(bool on) |
| 231 { |
| 232 if (on) { |
| 233 #ifdef CONFIG_S5PV210_PM |
| 234 if (s5pv210_pd_enable("vp_pd") < 0) { |
| 235 printk(KERN_ERR "[Err]Power is not on for VP\n"); |
| 236 goto err_pm; |
| 237 } |
| 238 #endif |
| 239 clk_enable(s5ptv_status.vp_clk); |
| 240 #ifdef CONFIG_S5PV210_PM |
| 241 if (s5pv210_pd_enable("mixer_pd") < 0) { |
| 242 printk(KERN_ERR "[Err]Power is not on for mixer\n"); |
| 243 goto err_pm; |
| 244 } |
| 245 #endif |
| 246 clk_enable(s5ptv_status.mixer_clk); |
| 247 #ifdef CONFIG_S5PV210_PM |
| 248 if (s5pv210_pd_enable("tv_enc_pd") < 0) { |
| 249 printk(KERN_ERR "[Err]Power is not on for TV ENC\n"); |
| 250 goto err_pm; |
| 251 } |
| 252 #endif |
| 253 clk_enable(s5ptv_status.tvenc_clk); |
| 254 #ifdef CONFIG_S5PV210_PM |
| 255 if (s5pv210_pd_enable("hdmi_pd") < 0) { |
| 256 printk(KERN_ERR "[Err]Power is not on for HDMI\n"); |
| 257 goto err_pm; |
| 258 } |
| 259 #endif |
| 260 clk_enable(s5ptv_status.hdmi_clk); |
| 261 |
| 262 } else { |
| 263 |
| 264 /* off */ |
| 265 clk_disable(s5ptv_status.vp_clk); |
| 266 #ifdef CONFIG_S5PV210_PM |
| 267 if (s5pv210_pd_disable("vp_pd") < 0) { |
| 268 printk(KERN_ERR "[Err]Power is not off for VP\n"); |
| 269 goto err_pm; |
| 270 } |
| 271 #endif |
| 272 clk_disable(s5ptv_status.mixer_clk); |
| 273 #ifdef CONFIG_S5PV210_PM |
| 274 if (0 != s5pv210_pd_disable("mixer_pd")) { |
| 275 printk(KERN_ERR "[Err]Power is not off for mixer\n"); |
| 276 goto err_pm; |
| 277 } |
| 278 #endif |
| 279 clk_disable(s5ptv_status.tvenc_clk); |
| 280 #ifdef CONFIG_S5PV210_PM |
| 281 if (s5pv210_pd_disable("tv_enc_pd") < 0) { |
| 282 printk(KERN_ERR "[Err]Power is not off for TV ENC\n"); |
| 283 goto err_pm; |
| 284 } |
| 285 #endif |
| 286 clk_disable(s5ptv_status.hdmi_clk); |
| 287 #ifdef CONFIG_S5PV210_PM |
| 288 if (s5pv210_pd_disable("hdmi_pd") < 0) { |
| 289 printk(KERN_ERR "[Err]Power is not off for HDMI\n"); |
| 290 goto err_pm; |
| 291 } |
| 292 #endif |
| 293 } |
| 294 |
| 295 return 0; |
| 296 #ifdef CONFIG_S5PV210_PM |
| 297 err_pm: |
| 298 return -1; |
| 299 #endif |
| 300 } |
| 301 |
| 302 static int __devinit tv_clk_get(struct platform_device *pdev, |
| 303 struct s5p_tv_status *ctrl) |
| 304 { |
| 305 /* tvenc clk */ |
| 306 ctrl->tvenc_clk = clk_get(&pdev->dev, "tvenc"); |
| 307 |
| 308 if (IS_ERR(ctrl->tvenc_clk)) { |
| 309 printk(KERN_ERR "failed to find %s clock source\n", "tvenc"); |
| 310 return -ENOENT; |
| 311 } |
| 312 |
| 313 /* vp clk */ |
| 314 ctrl->vp_clk = clk_get(&pdev->dev, "vp"); |
| 315 |
| 316 if (IS_ERR(ctrl->vp_clk)) { |
| 317 printk(KERN_ERR "failed to find %s clock source\n", "vp"); |
| 318 return -ENOENT; |
| 319 } |
| 320 |
| 321 /* mixer clk */ |
| 322 ctrl->mixer_clk = clk_get(&pdev->dev, "mixer"); |
| 323 |
| 324 if (IS_ERR(ctrl->mixer_clk)) { |
| 325 printk(KERN_ERR "failed to find %s clock source\n", "mixer"); |
| 326 return -ENOENT; |
| 327 } |
| 328 |
| 329 /* hdmi clk */ |
| 330 ctrl->hdmi_clk = clk_get(&pdev->dev, "hdmi"); |
| 331 |
| 332 if (IS_ERR(ctrl->hdmi_clk)) { |
| 333 printk(KERN_ERR "failed to find %s clock source\n", "hdmi"); |
| 334 return -ENOENT; |
| 335 } |
| 336 |
| 337 /* i2c-hdmiphy clk */ |
| 338 ctrl->i2c_phy_clk = clk_get(&pdev->dev, "i2c-hdmiphy"); |
| 339 |
| 340 if (IS_ERR(ctrl->i2c_phy_clk)) { |
| 341 printk(KERN_ERR |
| 342 "failed to find %s clock source\n", "i2c-hdmiphy"); |
| 343 return -ENOENT; |
| 344 } |
| 345 |
| 346 return 0; |
| 347 } |
| 348 |
| 349 /* |
| 350 * ftn for irq |
| 351 */ |
| 352 static irqreturn_t s5p_tvenc_irq(int irq, void *dev_id) |
| 353 { |
| 354 return IRQ_HANDLED; |
| 355 } |
| 356 |
| 357 static int s5p_tv_open(struct file *file) |
| 358 { |
| 359 /* |
| 360 * for first open |
| 361 * when boot up time this parameter is set. |
| 362 */ |
| 363 |
| 364 if (s5ptv_status.tvout_output_enable) |
| 365 tv_if_stop(); |
| 366 |
| 367 return 0; |
| 368 } |
| 369 |
| 370 static int s5p_tv_release(struct file *file) |
| 371 { |
| 372 s5ptv_status.hdcp_en = false; |
| 373 |
| 374 if (s5ptv_status.tvout_output_enable) |
| 375 tv_if_stop(); |
| 376 |
| 377 return 0; |
| 378 } |
| 379 |
| 380 static int s5p_tv_vid_open(struct file *file) |
| 381 { |
| 382 int ret = 0; |
| 383 |
| 384 mutex_lock(mutex_for_fo); |
| 385 |
| 386 if (s5ptv_status.vp_layer_enable) { |
| 387 printk(KERN_ERR "video layer. already used !!\n"); |
| 388 ret = -EBUSY; |
| 389 } |
| 390 |
| 391 mutex_unlock(mutex_for_fo); |
| 392 return ret; |
| 393 } |
| 394 |
| 395 static int s5p_tv_vid_release(struct file *file) |
| 396 { |
| 397 s5ptv_status.vp_layer_enable = false; |
| 398 |
| 399 tv_vlayer_stop(); |
| 400 |
| 401 return 0; |
| 402 } |
| 403 |
| 404 int s5ptvfb_alloc_framebuffer(void) |
| 405 { |
| 406 int ret; |
| 407 |
| 408 /* alloc for each framebuffer */ |
| 409 s5ptv_status.fb = framebuffer_alloc(sizeof(struct s5ptvfb_window), |
| 410 s5ptv_status.dev_fb); |
| 411 if (!s5ptv_status.fb) { |
| 412 dev_err(s5ptv_status.dev_fb, "not enough memory\n"); |
| 413 ret = -ENOMEM; |
| 414 goto err_alloc_fb; |
| 415 } |
| 416 |
| 417 /* Initializing framebuffer struct. */ |
| 418 memset(s5ptv_status.fb, 0, sizeof(struct s5ptvfb_window)); |
| 419 |
| 420 /* Changing frame number 0 into 5 for TV out */ |
| 421 ret = s5ptvfb_init_fbinfo(5); |
| 422 if (ret) { |
| 423 dev_err(s5ptv_status.dev_fb, |
| 424 "failed to allocate memory for fb for tv\n"); |
| 425 ret = -ENOMEM; |
| 426 goto err_alloc_fb; |
| 427 } |
| 428 |
| 429 return 0; |
| 430 |
| 431 err_alloc_fb: |
| 432 if (s5ptv_status.fb) |
| 433 framebuffer_release(s5ptv_status.fb); |
| 434 |
| 435 kfree(s5ptv_status.fb); |
| 436 |
| 437 return ret; |
| 438 } |
| 439 |
| 440 int s5ptvfb_free_framebuffer(void) |
| 441 { |
| 442 if (s5ptv_status.fb) |
| 443 framebuffer_release(s5ptv_status.fb); |
| 444 |
| 445 return 0; |
| 446 } |
| 447 |
| 448 int s5ptvfb_register_framebuffer(void) |
| 449 { |
| 450 int ret; |
| 451 |
| 452 ret = register_framebuffer(s5ptv_status.fb); |
| 453 if (ret) { |
| 454 dev_err(s5ptv_status.dev_fb, "failed to register " |
| 455 "framebuffer device\n"); |
| 456 return -EINVAL; |
| 457 } |
| 458 return 0; |
| 459 } |
| 460 |
| 461 int s5ptvfb_unregister_framebuffer(void) |
| 462 { |
| 463 if (s5ptv_status.fb) |
| 464 unregister_framebuffer(s5ptv_status.fb); |
| 465 |
| 466 return 0; |
| 467 } |
| 468 |
| 469 /* |
| 470 * struct for video |
| 471 */ |
| 472 static struct v4l2_file_operations s5p_tv_fops = { |
| 473 .owner = THIS_MODULE, |
| 474 .open = s5p_tv_open, |
| 475 .ioctl = s5p_tv_ioctl, |
| 476 .release = s5p_tv_release |
| 477 }; |
| 478 static struct v4l2_file_operations s5p_tv_vid_fops = { |
| 479 .owner = THIS_MODULE, |
| 480 .open = s5p_tv_vid_open, |
| 481 .ioctl = s5p_tv_vid_ioctl, |
| 482 .release = s5p_tv_vid_release |
| 483 }; |
| 484 |
| 485 |
| 486 void s5p_tv_vdev_release(struct video_device *vdev) |
| 487 { |
| 488 kfree(vdev); |
| 489 } |
| 490 |
| 491 struct video_device s5p_tvout[] = { |
| 492 [0] = { |
| 493 .name = "S5PV210 TVOUT ctrl", |
| 494 .fops = &s5p_tv_fops, |
| 495 .ioctl_ops = &s5p_tv_v4l2_ops, |
| 496 .release = s5p_tv_vdev_release, |
| 497 .minor = TVOUT_MINOR_TVOUT, |
| 498 .tvnorms = V4L2_STD_ALL_HD, |
| 499 }, |
| 500 [1] = { |
| 501 .name = "S5PV210 TVOUT for Video", |
| 502 .fops = &s5p_tv_vid_fops, |
| 503 .ioctl_ops = &s5p_tv_v4l2_vid_ops, |
| 504 .release = s5p_tv_vdev_release, |
| 505 .minor = TVOUT_MINOR_VID, |
| 506 .tvnorms = V4L2_STD_ALL_HD, |
| 507 }, |
| 508 }; |
| 509 |
| 510 void s5p_tv_kobject_uevent(struct work_struct *data) |
| 511 { |
| 512 int hpd_state = s5p_hpd_get_state(); |
| 513 |
| 514 if (hpd_state) { |
| 515 printk(KERN_ERR "Event] Send UEvent = %d\n", hpd_state); |
| 516 kobject_uevent(&(s5p_tvout[0].dev.kobj), KOBJ_ADD); |
| 517 kobject_uevent(&(s5p_tvout[1].dev.kobj), KOBJ_ADD); |
| 518 } else { |
| 519 printk(KERN_ERR "Event] Send UEvent = %d\n", hpd_state); |
| 520 kobject_uevent(&(s5p_tvout[0].dev.kobj), KOBJ_REMOVE); |
| 521 kobject_uevent(&(s5p_tvout[1].dev.kobj), KOBJ_REMOVE); |
| 522 } |
| 523 |
| 524 } |
| 525 |
| 526 |
| 527 |
| 528 #define S5P_TVMAX_CTRLS ARRAY_SIZE(s5p_tvout) |
| 529 |
| 530 static int __devinit s5p_tv_probe(struct platform_device *pdev) |
| 531 { |
| 532 int irq_num; |
| 533 int ret; |
| 534 int i; |
| 535 |
| 536 s5ptv_status.dev_fb = &pdev->dev; |
| 537 |
| 538 tv_sdout_probe(pdev, 0); |
| 539 tv_vp_probe(pdev, 1); |
| 540 tv_mixer_probe(pdev, 2); |
| 541 |
| 542 tv_hdmi_probe(pdev, 3, 4); |
| 543 |
| 544 tv_hdcp_init(); |
| 545 |
| 546 tv_clk_get(pdev, &s5ptv_status); |
| 547 |
| 548 #ifdef FIX_27M_UNSTABLE_ISSUE /* for smdkc100 pop */ |
| 549 writel(0x1, S5PC1XX_GPA0_BASE + 0x56c); |
| 550 #endif |
| 551 |
| 552 #ifdef I2C_BASE |
| 553 /* for dev_dbg err. */ |
| 554 spin_lock_init(&slock_hpd); |
| 555 |
| 556 /* for bh */ |
| 557 INIT_WORK(&ws_hpd, (void *)set_ddc_port); |
| 558 #endif |
| 559 /* interrupt */ |
| 560 /* Changing interrupt mode into SHARED */ |
| 561 irq_num = platform_get_irq(pdev, 0); |
| 562 if (irq_num < 0) { |
| 563 printk(KERN_ERR "failed to get %s irq resource\n", "mixer"); |
| 564 ret = -ENOENT; |
| 565 goto out; |
| 566 } |
| 567 |
| 568 ret = request_irq(irq_num, tv_mixer_irq, |
| 569 IRQF_SHARED, pdev->name, pdev); |
| 570 if (ret != 0) { |
| 571 printk(KERN_ERR "failed to install %s irq (%d)\n", "mixer", |
| 572 ret); |
| 573 goto out; |
| 574 } |
| 575 |
| 576 TVOUT_IRQ_INIT(irq_num, ret, pdev, 1, out_hdmi_irq, |
| 577 tv_hdmi_irq , "hdmi"); |
| 578 TVOUT_IRQ_INIT(irq_num, ret, pdev, 2, out_tvenc_irq, |
| 579 s5p_tvenc_irq, "tvenc"); |
| 580 |
| 581 set_irq_type(IRQ_EINT5, IRQ_TYPE_LEVEL_LOW); |
| 582 |
| 583 /* v4l2 video device registration */ |
| 584 for (i = 0; i < S5P_TVMAX_CTRLS; i++) { |
| 585 s5ptv_status.video_dev[i] = &s5p_tvout[i]; |
| 586 |
| 587 if (video_register_device(s5ptv_status.video_dev[i], |
| 588 VFL_TYPE_GRABBER, s5p_tvout[i].minor) != 0) { |
| 589 |
| 590 dev_err(&pdev->dev, |
| 591 "Couldn't register tvout driver.\n"); |
| 592 return 0; |
| 593 } |
| 594 } |
| 595 |
| 596 /* for default start up */ |
| 597 tv_if_init_param(); |
| 598 |
| 599 s5ptv_status.tvout_param.disp_mode = TVOUT_720P_60; |
| 600 s5ptv_status.tvout_param.out_mode = TVOUT_OUTPUT_HDMI_RGB; |
| 601 |
| 602 mutex_init(&s5ptv_status.fb_lock); |
| 603 |
| 604 s5ptvfb_set_lcd_info(&s5ptv_status); |
| 605 |
| 606 /* prepare memory */ |
| 607 if (s5ptvfb_alloc_framebuffer()) |
| 608 goto err_alloc; |
| 609 |
| 610 if (s5ptvfb_register_framebuffer()) |
| 611 goto err_alloc; |
| 612 |
| 613 mutex_for_fo = |
| 614 kmalloc(sizeof(struct mutex), GFP_KERNEL); |
| 615 |
| 616 if (mutex_for_fo == NULL) { |
| 617 dev_err(&pdev->dev, |
| 618 "failed to create mutex handle\n"); |
| 619 goto out; |
| 620 } |
| 621 |
| 622 #ifdef I2C_BASE |
| 623 mutex_for_i2c = |
| 624 kmalloc(sizeof(struct mutex), GFP_KERNEL); |
| 625 |
| 626 if (mutex_for_i2c == NULL) { |
| 627 dev_err(&pdev->dev, |
| 628 "failed to create mutex handle\n"); |
| 629 goto out; |
| 630 } |
| 631 mutex_init(mutex_for_i2c); |
| 632 #endif |
| 633 mutex_init(mutex_for_fo); |
| 634 |
| 635 return 0; |
| 636 |
| 637 err_alloc: |
| 638 |
| 639 out_tvenc_irq: |
| 640 free_irq(IRQ_HDMI, pdev); |
| 641 |
| 642 out_hdmi_irq: |
| 643 free_irq(IRQ_MIXER, pdev); |
| 644 |
| 645 out: |
| 646 printk(KERN_ERR "not found (%d). \n", ret); |
| 647 |
| 648 return ret; |
| 649 } |
| 650 |
| 651 static int s5p_tv_remove(struct platform_device *pdev) |
| 652 { |
| 653 tv_hdmi_release(pdev); |
| 654 tv_sdout_release(pdev); |
| 655 tv_mixer_release(pdev); |
| 656 tv_vp_release(pdev); |
| 657 |
| 658 #ifdef I2C_BASE |
| 659 i2c_del_driver(&hdcp_i2c_driver); |
| 660 #endif |
| 661 clk_disable(s5ptv_status.tvenc_clk); |
| 662 clk_disable(s5ptv_status.vp_clk); |
| 663 clk_disable(s5ptv_status.mixer_clk); |
| 664 clk_disable(s5ptv_status.hdmi_clk); |
| 665 clk_disable(s5ptv_status.sclk_hdmi); |
| 666 clk_disable(s5ptv_status.sclk_mixer); |
| 667 clk_disable(s5ptv_status.sclk_tv); |
| 668 |
| 669 clk_put(s5ptv_status.tvenc_clk); |
| 670 clk_put(s5ptv_status.vp_clk); |
| 671 clk_put(s5ptv_status.mixer_clk); |
| 672 clk_put(s5ptv_status.hdmi_clk); |
| 673 clk_put(s5ptv_status.sclk_hdmi); |
| 674 clk_put(s5ptv_status.sclk_mixer); |
| 675 clk_put(s5ptv_status.sclk_tv); |
| 676 |
| 677 free_irq(IRQ_MIXER, pdev); |
| 678 free_irq(IRQ_HDMI, pdev); |
| 679 free_irq(IRQ_TVENC, pdev); |
| 680 free_irq(IRQ_EINT5, pdev); |
| 681 |
| 682 mutex_destroy(mutex_for_fo); |
| 683 #ifdef I2C_BASE |
| 684 mutex_destroy(mutex_for_i2c); |
| 685 #endif |
| 686 |
| 687 s5ptvfb_unregister_framebuffer(); |
| 688 s5ptvfb_free_framebuffer(); |
| 689 return 0; |
| 690 } |
| 691 |
| 692 |
| 693 #ifdef CONFIG_PM |
| 694 int s5p_tv_suspend(struct platform_device *dev, pm_message_t state) |
| 695 { |
| 696 /* video layer stop */ |
| 697 if (s5ptv_status.vp_layer_enable) { |
| 698 tv_vlayer_stop(); |
| 699 s5ptv_status.vp_layer_enable = true; |
| 700 |
| 701 } |
| 702 |
| 703 /* grp0 layer stop */ |
| 704 if (s5ptv_status.grp_layer_enable[0]) { |
| 705 tv_grp_stop(VM_GPR0_LAYER); |
| 706 s5ptv_status.grp_layer_enable[VM_GPR0_LAYER] = true; |
| 707 } |
| 708 |
| 709 /* grp1 layer stop */ |
| 710 if (s5ptv_status.grp_layer_enable[1]) { |
| 711 tv_grp_stop(VM_GPR1_LAYER); |
| 712 s5ptv_status.grp_layer_enable[VM_GPR0_LAYER] = true; |
| 713 } |
| 714 |
| 715 /* tv off */ |
| 716 if (s5ptv_status.tvout_output_enable) { |
| 717 tv_if_stop(); |
| 718 s5ptv_status.tvout_output_enable = true; |
| 719 s5ptv_status.tvout_param_available = true; |
| 720 |
| 721 /* clk & power off */ |
| 722 if (s5p_tv_clk_gate(false) < 0) { |
| 723 printk(KERN_ERR "[Error]Cannot suspend\n"); |
| 724 return -1; |
| 725 } |
| 726 tv_phy_power(false); |
| 727 |
| 728 } |
| 729 |
| 730 return 0; |
| 731 } |
| 732 |
| 733 int s5p_tv_resume(struct platform_device *dev) |
| 734 { |
| 735 /* tv on */ |
| 736 if (s5ptv_status.tvout_output_enable) { |
| 737 |
| 738 /* clk & power on */ |
| 739 if (s5p_tv_clk_gate(true) < 0) { |
| 740 printk(KERN_ERR "[Error]Cannot resume\n"); |
| 741 return -1; |
| 742 } |
| 743 tv_phy_power(true); |
| 744 |
| 745 tv_if_start(); |
| 746 } |
| 747 |
| 748 /* video layer start */ |
| 749 if (s5ptv_status.vp_layer_enable) |
| 750 tv_vlayer_start(); |
| 751 |
| 752 /* grp0 layer start */ |
| 753 if (s5ptv_status.grp_layer_enable[0]) |
| 754 tv_grp_start(VM_GPR0_LAYER); |
| 755 |
| 756 /* grp1 layer start */ |
| 757 if (s5ptv_status.grp_layer_enable[1]) |
| 758 tv_grp_start(VM_GPR1_LAYER); |
| 759 |
| 760 s5ptvfb_display_on(&s5ptv_status); |
| 761 s5ptvfb_set_par(s5ptv_status.fb); |
| 762 |
| 763 return 0; |
| 764 } |
| 765 #else |
| 766 #define s5p_tv_suspend NULL |
| 767 #define s5p_tv_resume NULL |
| 768 #endif |
| 769 |
| 770 static struct platform_driver s5p_tv_driver = { |
| 771 .probe = s5p_tv_probe, |
| 772 .remove = s5p_tv_remove, |
| 773 .suspend = s5p_tv_suspend, |
| 774 .resume = s5p_tv_resume, |
| 775 .driver = { |
| 776 .name = "s5p-tvout", |
| 777 .owner = THIS_MODULE, |
| 778 }, |
| 779 }; |
| 780 |
| 781 static char banner[] __initdata = |
| 782 KERN_INFO "S5V210 TVOUT Driver, (c) 2010 Samsung Electronics\n"; |
| 783 |
| 784 int __init s5p_tv_init(void) |
| 785 { |
| 786 int ret; |
| 787 |
| 788 printk(banner); |
| 789 |
| 790 ret = platform_driver_register(&s5p_tv_driver); |
| 791 |
| 792 if (ret) { |
| 793 printk(KERN_ERR "Platform Device Register Failed %d\n", ret); |
| 794 return -1; |
| 795 } |
| 796 |
| 797 return 0; |
| 798 } |
| 799 |
| 800 static void __exit s5p_tv_exit(void) |
| 801 { |
| 802 platform_driver_unregister(&s5p_tv_driver); |
| 803 } |
| 804 |
| 805 late_initcall(s5p_tv_init); |
| 806 module_exit(s5p_tv_exit); |
| 807 |
| 808 MODULE_AUTHOR("SangPil Moon"); |
| 809 MODULE_DESCRIPTION("S5V210 TVOUT driver"); |
| 810 MODULE_LICENSE("GPL"); |
OLD | NEW |