Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(312)

Unified Diff: drivers/media/video/samsung/tv20/s5p_tv_base.c

Issue 2036011: V4L/DVB : Add S5PV210 TV out driver support (Closed) Base URL: swsolcc@12.23.106.100:kernel-samsung.git
Patch Set: Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « drivers/media/video/samsung/tv20/s5p_tv.h ('k') | drivers/media/video/samsung/tv20/s5p_tv_v4l2.c » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: drivers/media/video/samsung/tv20/s5p_tv_base.c
diff --git a/drivers/media/video/samsung/tv20/s5p_tv_base.c b/drivers/media/video/samsung/tv20/s5p_tv_base.c
new file mode 100644
index 0000000000000000000000000000000000000000..df1e9a0fc1ddb9ef0ae28940126c27e74f75705e
--- /dev/null
+++ b/drivers/media/video/samsung/tv20/s5p_tv_base.c
@@ -0,0 +1,810 @@
+/* linux/drivers/media/video/samsung/tv20/s5p_tv_base.c
+*
+* Copyright (c) 2010 Samsung Electronics Co., Ltd.
+* http://www.samsung.com/
+*
+* S5PV210 - Entry file for Samsung TVOut driver
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/ioctl.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <mach/map.h>
+
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+
+#include <linux/io.h>
+#include <linux/uaccess.h>
+
+#ifdef CONFIG_S5PV210_PM
+#include <mach/pd.h>
+#endif
+
+#include "s5p_tv.h"
+
+#ifdef CONFIG_TVOUT_DBG
+#define S5P_TV_BASE_DEBUG 1
+#endif
+
+#ifdef S5P_TV_BASE_DEBUG
+#define BASEPRINTK(fmt, args...) \
+ printk(KERN_INFO "[TVBASE] %s: " fmt, __func__ , ## args)
+#else
+#define BASEPRINTK(fmt, args...)
+#endif
+
+
+#define TVOUT_CLK_INIT(dev, clk, name)
+
+#define TVOUT_IRQ_INIT(x, ret, dev, num, jump, ftn, m_name) \
+ do { \
+ x = platform_get_irq(dev, num); \
+ if (x < 0) { \
+ printk(KERN_ERR \
+ "failed to get %s irq resource\n", m_name); \
+ ret = -ENOENT; \
+ goto jump; \
+ } \
+ ret = request_irq(x, ftn, IRQF_DISABLED, dev->name, dev); \
+ if (ret != 0) { \
+ printk(KERN_ERR \
+ "failed to install %s irq (%d)\n", m_name, ret); \
+ goto jump; \
+ } \
+ } while (0)
+
+
+static struct mutex *mutex_for_fo;
+
+
+struct s5p_tv_status s5ptv_status;
+struct s5p_tv_vo s5ptv_overlay[2];
+wait_queue_head_t s5ptv_wq;
+
+#ifdef I2C_BASE
+static struct mutex *mutex_for_i2c;
+static struct work_struct ws_hpd;
+spinlock_t slock_hpd;
+
+static struct i2c_driver hdcp_i2c_driver;
+static bool hdcp_i2c_drv_state;
+
+const static u16 ignore[] = {I2C_CLIENT_END};
+const static u16 normal_addr[] = {(S5P_HDCP_I2C_ADDR >> 1), I2C_CLIENT_END};
+const static u16 *forces[] = {NULL};
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_addr,
+ .probe = ignore,
+ .ignore = ignore,
+ .forces = forces,
+};
+
+/*
+ * i2c client drv. - register client drv
+ */
+static int hdcp_i2c_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+
+ struct i2c_client *c;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+
+ if (!c)
+ return -ENOMEM;
+
+ strcpy(c->name, "s5p_ddc_client");
+
+ c->addr = addr;
+
+ c->adapter = adap;
+
+ c->driver = &hdcp_i2c_driver;
+
+ s5ptv_status.hdcp_i2c_client = c;
+
+ dev_info(&adap->dev, "s5p_ddc_client attached "
+ "into s5p_ddc_port successfully\n");
+
+ return i2c_attach_client(c);
+}
+
+static int hdcp_i2c_attach_adapter(struct i2c_adapter *adap)
+{
+ int ret = 0;
+
+ ret = i2c_probe(adap, &addr_data, hdcp_i2c_attach);
+
+ if (ret) {
+ dev_err(&adap->dev,
+ "failed to attach s5p_hdcp_port driver\n");
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+static int hdcp_i2c_detach(struct i2c_client *client)
+{
+ dev_info(&client->adapter->dev, "s5p_ddc_client detached "
+ "from s5p_ddc_port successfully\n");
+
+ i2c_detach_client(client);
+
+ return 0;
+}
+
+static struct i2c_driver hdcp_i2c_driver = {
+ .driver = {
+ .name = "s5p_ddc_port",
+ },
+ .id = I2C_DRIVERID_S5P_HDCP,
+ .attach_adapter = hdcp_i2c_attach_adapter,
+ .detach_client = hdcp_i2c_detach,
+};
+
+static void set_ddc_port(void)
+{
+ mutex_lock(mutex_for_i2c);
+
+ if (s5ptv_status.hpd_status) {
+
+ if (!hdcp_i2c_drv_state)
+ /* cable : plugged, drv : unregistered */
+ if (i2c_add_driver(&hdcp_i2c_driver))
+ printk(KERN_INFO "HDCP port add failed\n");
+
+ /* changed drv. status */
+ hdcp_i2c_drv_state = true;
+
+
+ /* cable inserted -> removed */
+ tv_set_hpd_detection(true, s5ptv_status.hdcp_en,
+ s5ptv_status.hdcp_i2c_client);
+
+ } else {
+
+ if (hdcp_i2c_drv_state)
+ /* cable : unplugged, drv : registered */
+ i2c_del_driver(&hdcp_i2c_driver);
+
+ /* changed drv. status */
+ hdcp_i2c_drv_state = false;
+
+ /* cable removed -> inserted */
+ tv_set_hpd_detection(false, s5ptv_status.hdcp_en,
+ s5ptv_status.hdcp_i2c_client);
+ }
+
+ mutex_unlock(mutex_for_i2c);
+}
+#endif
+
+int tv_phy_power(bool on)
+{
+ if (on) {
+ /* on */
+ clk_enable(s5ptv_status.i2c_phy_clk);
+
+ tv_hdmi_phy_power(true);
+
+ } else {
+ /*
+ * for preventing hdmi hang up when restart
+ * switch to internal clk - SCLK_DAC, SCLK_PIXEL
+ */
+ tv_clk_change_internal();
+
+ tv_hdmi_phy_power(false);
+
+ clk_disable(s5ptv_status.i2c_phy_clk);
+ }
+
+ return 0;
+}
+
+
+int s5p_tv_clk_gate(bool on)
+{
+ if (on) {
+#ifdef CONFIG_S5PV210_PM
+ if (s5pv210_pd_enable("vp_pd") < 0) {
+ printk(KERN_ERR "[Err]Power is not on for VP\n");
+ goto err_pm;
+ }
+#endif
+ clk_enable(s5ptv_status.vp_clk);
+#ifdef CONFIG_S5PV210_PM
+ if (s5pv210_pd_enable("mixer_pd") < 0) {
+ printk(KERN_ERR "[Err]Power is not on for mixer\n");
+ goto err_pm;
+ }
+#endif
+ clk_enable(s5ptv_status.mixer_clk);
+#ifdef CONFIG_S5PV210_PM
+ if (s5pv210_pd_enable("tv_enc_pd") < 0) {
+ printk(KERN_ERR "[Err]Power is not on for TV ENC\n");
+ goto err_pm;
+ }
+#endif
+ clk_enable(s5ptv_status.tvenc_clk);
+#ifdef CONFIG_S5PV210_PM
+ if (s5pv210_pd_enable("hdmi_pd") < 0) {
+ printk(KERN_ERR "[Err]Power is not on for HDMI\n");
+ goto err_pm;
+ }
+#endif
+ clk_enable(s5ptv_status.hdmi_clk);
+
+ } else {
+
+ /* off */
+ clk_disable(s5ptv_status.vp_clk);
+#ifdef CONFIG_S5PV210_PM
+ if (s5pv210_pd_disable("vp_pd") < 0) {
+ printk(KERN_ERR "[Err]Power is not off for VP\n");
+ goto err_pm;
+ }
+#endif
+ clk_disable(s5ptv_status.mixer_clk);
+#ifdef CONFIG_S5PV210_PM
+ if (0 != s5pv210_pd_disable("mixer_pd")) {
+ printk(KERN_ERR "[Err]Power is not off for mixer\n");
+ goto err_pm;
+ }
+#endif
+ clk_disable(s5ptv_status.tvenc_clk);
+#ifdef CONFIG_S5PV210_PM
+ if (s5pv210_pd_disable("tv_enc_pd") < 0) {
+ printk(KERN_ERR "[Err]Power is not off for TV ENC\n");
+ goto err_pm;
+ }
+#endif
+ clk_disable(s5ptv_status.hdmi_clk);
+#ifdef CONFIG_S5PV210_PM
+ if (s5pv210_pd_disable("hdmi_pd") < 0) {
+ printk(KERN_ERR "[Err]Power is not off for HDMI\n");
+ goto err_pm;
+ }
+#endif
+ }
+
+ return 0;
+#ifdef CONFIG_S5PV210_PM
+err_pm:
+ return -1;
+#endif
+}
+
+static int __devinit tv_clk_get(struct platform_device *pdev,
+ struct s5p_tv_status *ctrl)
+{
+ /* tvenc clk */
+ ctrl->tvenc_clk = clk_get(&pdev->dev, "tvenc");
+
+ if (IS_ERR(ctrl->tvenc_clk)) {
+ printk(KERN_ERR "failed to find %s clock source\n", "tvenc");
+ return -ENOENT;
+ }
+
+ /* vp clk */
+ ctrl->vp_clk = clk_get(&pdev->dev, "vp");
+
+ if (IS_ERR(ctrl->vp_clk)) {
+ printk(KERN_ERR "failed to find %s clock source\n", "vp");
+ return -ENOENT;
+ }
+
+ /* mixer clk */
+ ctrl->mixer_clk = clk_get(&pdev->dev, "mixer");
+
+ if (IS_ERR(ctrl->mixer_clk)) {
+ printk(KERN_ERR "failed to find %s clock source\n", "mixer");
+ return -ENOENT;
+ }
+
+ /* hdmi clk */
+ ctrl->hdmi_clk = clk_get(&pdev->dev, "hdmi");
+
+ if (IS_ERR(ctrl->hdmi_clk)) {
+ printk(KERN_ERR "failed to find %s clock source\n", "hdmi");
+ return -ENOENT;
+ }
+
+ /* i2c-hdmiphy clk */
+ ctrl->i2c_phy_clk = clk_get(&pdev->dev, "i2c-hdmiphy");
+
+ if (IS_ERR(ctrl->i2c_phy_clk)) {
+ printk(KERN_ERR
+ "failed to find %s clock source\n", "i2c-hdmiphy");
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+/*
+ * ftn for irq
+ */
+static irqreturn_t s5p_tvenc_irq(int irq, void *dev_id)
+{
+ return IRQ_HANDLED;
+}
+
+static int s5p_tv_open(struct file *file)
+{
+ /*
+ * for first open
+ * when boot up time this parameter is set.
+ */
+
+ if (s5ptv_status.tvout_output_enable)
+ tv_if_stop();
+
+ return 0;
+}
+
+static int s5p_tv_release(struct file *file)
+{
+ s5ptv_status.hdcp_en = false;
+
+ if (s5ptv_status.tvout_output_enable)
+ tv_if_stop();
+
+ return 0;
+}
+
+static int s5p_tv_vid_open(struct file *file)
+{
+ int ret = 0;
+
+ mutex_lock(mutex_for_fo);
+
+ if (s5ptv_status.vp_layer_enable) {
+ printk(KERN_ERR "video layer. already used !!\n");
+ ret = -EBUSY;
+ }
+
+ mutex_unlock(mutex_for_fo);
+ return ret;
+}
+
+static int s5p_tv_vid_release(struct file *file)
+{
+ s5ptv_status.vp_layer_enable = false;
+
+ tv_vlayer_stop();
+
+ return 0;
+}
+
+int s5ptvfb_alloc_framebuffer(void)
+{
+ int ret;
+
+ /* alloc for each framebuffer */
+ s5ptv_status.fb = framebuffer_alloc(sizeof(struct s5ptvfb_window),
+ s5ptv_status.dev_fb);
+ if (!s5ptv_status.fb) {
+ dev_err(s5ptv_status.dev_fb, "not enough memory\n");
+ ret = -ENOMEM;
+ goto err_alloc_fb;
+ }
+
+ /* Initializing framebuffer struct. */
+ memset(s5ptv_status.fb, 0, sizeof(struct s5ptvfb_window));
+
+ /* Changing frame number 0 into 5 for TV out */
+ ret = s5ptvfb_init_fbinfo(5);
+ if (ret) {
+ dev_err(s5ptv_status.dev_fb,
+ "failed to allocate memory for fb for tv\n");
+ ret = -ENOMEM;
+ goto err_alloc_fb;
+ }
+
+ return 0;
+
+err_alloc_fb:
+ if (s5ptv_status.fb)
+ framebuffer_release(s5ptv_status.fb);
+
+ kfree(s5ptv_status.fb);
+
+ return ret;
+}
+
+int s5ptvfb_free_framebuffer(void)
+{
+ if (s5ptv_status.fb)
+ framebuffer_release(s5ptv_status.fb);
+
+ return 0;
+}
+
+int s5ptvfb_register_framebuffer(void)
+{
+ int ret;
+
+ ret = register_framebuffer(s5ptv_status.fb);
+ if (ret) {
+ dev_err(s5ptv_status.dev_fb, "failed to register "
+ "framebuffer device\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int s5ptvfb_unregister_framebuffer(void)
+{
+ if (s5ptv_status.fb)
+ unregister_framebuffer(s5ptv_status.fb);
+
+ return 0;
+}
+
+/*
+ * struct for video
+ */
+static struct v4l2_file_operations s5p_tv_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_tv_open,
+ .ioctl = s5p_tv_ioctl,
+ .release = s5p_tv_release
+};
+static struct v4l2_file_operations s5p_tv_vid_fops = {
+ .owner = THIS_MODULE,
+ .open = s5p_tv_vid_open,
+ .ioctl = s5p_tv_vid_ioctl,
+ .release = s5p_tv_vid_release
+};
+
+
+void s5p_tv_vdev_release(struct video_device *vdev)
+{
+ kfree(vdev);
+}
+
+struct video_device s5p_tvout[] = {
+ [0] = {
+ .name = "S5PV210 TVOUT ctrl",
+ .fops = &s5p_tv_fops,
+ .ioctl_ops = &s5p_tv_v4l2_ops,
+ .release = s5p_tv_vdev_release,
+ .minor = TVOUT_MINOR_TVOUT,
+ .tvnorms = V4L2_STD_ALL_HD,
+ },
+ [1] = {
+ .name = "S5PV210 TVOUT for Video",
+ .fops = &s5p_tv_vid_fops,
+ .ioctl_ops = &s5p_tv_v4l2_vid_ops,
+ .release = s5p_tv_vdev_release,
+ .minor = TVOUT_MINOR_VID,
+ .tvnorms = V4L2_STD_ALL_HD,
+ },
+};
+
+void s5p_tv_kobject_uevent(struct work_struct *data)
+{
+ int hpd_state = s5p_hpd_get_state();
+
+ if (hpd_state) {
+ printk(KERN_ERR "Event] Send UEvent = %d\n", hpd_state);
+ kobject_uevent(&(s5p_tvout[0].dev.kobj), KOBJ_ADD);
+ kobject_uevent(&(s5p_tvout[1].dev.kobj), KOBJ_ADD);
+ } else {
+ printk(KERN_ERR "Event] Send UEvent = %d\n", hpd_state);
+ kobject_uevent(&(s5p_tvout[0].dev.kobj), KOBJ_REMOVE);
+ kobject_uevent(&(s5p_tvout[1].dev.kobj), KOBJ_REMOVE);
+ }
+
+}
+
+
+
+#define S5P_TVMAX_CTRLS ARRAY_SIZE(s5p_tvout)
+
+static int __devinit s5p_tv_probe(struct platform_device *pdev)
+{
+ int irq_num;
+ int ret;
+ int i;
+
+ s5ptv_status.dev_fb = &pdev->dev;
+
+ tv_sdout_probe(pdev, 0);
+ tv_vp_probe(pdev, 1);
+ tv_mixer_probe(pdev, 2);
+
+ tv_hdmi_probe(pdev, 3, 4);
+
+ tv_hdcp_init();
+
+ tv_clk_get(pdev, &s5ptv_status);
+
+#ifdef FIX_27M_UNSTABLE_ISSUE /* for smdkc100 pop */
+ writel(0x1, S5PC1XX_GPA0_BASE + 0x56c);
+#endif
+
+#ifdef I2C_BASE
+ /* for dev_dbg err. */
+ spin_lock_init(&slock_hpd);
+
+ /* for bh */
+ INIT_WORK(&ws_hpd, (void *)set_ddc_port);
+#endif
+ /* interrupt */
+ /* Changing interrupt mode into SHARED */
+ irq_num = platform_get_irq(pdev, 0);
+ if (irq_num < 0) {
+ printk(KERN_ERR "failed to get %s irq resource\n", "mixer");
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = request_irq(irq_num, tv_mixer_irq,
+ IRQF_SHARED, pdev->name, pdev);
+ if (ret != 0) {
+ printk(KERN_ERR "failed to install %s irq (%d)\n", "mixer",
+ ret);
+ goto out;
+ }
+
+ TVOUT_IRQ_INIT(irq_num, ret, pdev, 1, out_hdmi_irq,
+ tv_hdmi_irq , "hdmi");
+ TVOUT_IRQ_INIT(irq_num, ret, pdev, 2, out_tvenc_irq,
+ s5p_tvenc_irq, "tvenc");
+
+ set_irq_type(IRQ_EINT5, IRQ_TYPE_LEVEL_LOW);
+
+ /* v4l2 video device registration */
+ for (i = 0; i < S5P_TVMAX_CTRLS; i++) {
+ s5ptv_status.video_dev[i] = &s5p_tvout[i];
+
+ if (video_register_device(s5ptv_status.video_dev[i],
+ VFL_TYPE_GRABBER, s5p_tvout[i].minor) != 0) {
+
+ dev_err(&pdev->dev,
+ "Couldn't register tvout driver.\n");
+ return 0;
+ }
+ }
+
+ /* for default start up */
+ tv_if_init_param();
+
+ s5ptv_status.tvout_param.disp_mode = TVOUT_720P_60;
+ s5ptv_status.tvout_param.out_mode = TVOUT_OUTPUT_HDMI_RGB;
+
+ mutex_init(&s5ptv_status.fb_lock);
+
+ s5ptvfb_set_lcd_info(&s5ptv_status);
+
+ /* prepare memory */
+ if (s5ptvfb_alloc_framebuffer())
+ goto err_alloc;
+
+ if (s5ptvfb_register_framebuffer())
+ goto err_alloc;
+
+ mutex_for_fo =
+ kmalloc(sizeof(struct mutex), GFP_KERNEL);
+
+ if (mutex_for_fo == NULL) {
+ dev_err(&pdev->dev,
+ "failed to create mutex handle\n");
+ goto out;
+ }
+
+#ifdef I2C_BASE
+ mutex_for_i2c =
+ kmalloc(sizeof(struct mutex), GFP_KERNEL);
+
+ if (mutex_for_i2c == NULL) {
+ dev_err(&pdev->dev,
+ "failed to create mutex handle\n");
+ goto out;
+ }
+ mutex_init(mutex_for_i2c);
+#endif
+ mutex_init(mutex_for_fo);
+
+ return 0;
+
+err_alloc:
+
+out_tvenc_irq:
+ free_irq(IRQ_HDMI, pdev);
+
+out_hdmi_irq:
+ free_irq(IRQ_MIXER, pdev);
+
+out:
+ printk(KERN_ERR "not found (%d). \n", ret);
+
+ return ret;
+}
+
+static int s5p_tv_remove(struct platform_device *pdev)
+{
+ tv_hdmi_release(pdev);
+ tv_sdout_release(pdev);
+ tv_mixer_release(pdev);
+ tv_vp_release(pdev);
+
+#ifdef I2C_BASE
+ i2c_del_driver(&hdcp_i2c_driver);
+#endif
+ clk_disable(s5ptv_status.tvenc_clk);
+ clk_disable(s5ptv_status.vp_clk);
+ clk_disable(s5ptv_status.mixer_clk);
+ clk_disable(s5ptv_status.hdmi_clk);
+ clk_disable(s5ptv_status.sclk_hdmi);
+ clk_disable(s5ptv_status.sclk_mixer);
+ clk_disable(s5ptv_status.sclk_tv);
+
+ clk_put(s5ptv_status.tvenc_clk);
+ clk_put(s5ptv_status.vp_clk);
+ clk_put(s5ptv_status.mixer_clk);
+ clk_put(s5ptv_status.hdmi_clk);
+ clk_put(s5ptv_status.sclk_hdmi);
+ clk_put(s5ptv_status.sclk_mixer);
+ clk_put(s5ptv_status.sclk_tv);
+
+ free_irq(IRQ_MIXER, pdev);
+ free_irq(IRQ_HDMI, pdev);
+ free_irq(IRQ_TVENC, pdev);
+ free_irq(IRQ_EINT5, pdev);
+
+ mutex_destroy(mutex_for_fo);
+#ifdef I2C_BASE
+ mutex_destroy(mutex_for_i2c);
+#endif
+
+ s5ptvfb_unregister_framebuffer();
+ s5ptvfb_free_framebuffer();
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+int s5p_tv_suspend(struct platform_device *dev, pm_message_t state)
+{
+ /* video layer stop */
+ if (s5ptv_status.vp_layer_enable) {
+ tv_vlayer_stop();
+ s5ptv_status.vp_layer_enable = true;
+
+ }
+
+ /* grp0 layer stop */
+ if (s5ptv_status.grp_layer_enable[0]) {
+ tv_grp_stop(VM_GPR0_LAYER);
+ s5ptv_status.grp_layer_enable[VM_GPR0_LAYER] = true;
+ }
+
+ /* grp1 layer stop */
+ if (s5ptv_status.grp_layer_enable[1]) {
+ tv_grp_stop(VM_GPR1_LAYER);
+ s5ptv_status.grp_layer_enable[VM_GPR0_LAYER] = true;
+ }
+
+ /* tv off */
+ if (s5ptv_status.tvout_output_enable) {
+ tv_if_stop();
+ s5ptv_status.tvout_output_enable = true;
+ s5ptv_status.tvout_param_available = true;
+
+ /* clk & power off */
+ if (s5p_tv_clk_gate(false) < 0) {
+ printk(KERN_ERR "[Error]Cannot suspend\n");
+ return -1;
+ }
+ tv_phy_power(false);
+
+ }
+
+ return 0;
+}
+
+int s5p_tv_resume(struct platform_device *dev)
+{
+ /* tv on */
+ if (s5ptv_status.tvout_output_enable) {
+
+ /* clk & power on */
+ if (s5p_tv_clk_gate(true) < 0) {
+ printk(KERN_ERR "[Error]Cannot resume\n");
+ return -1;
+ }
+ tv_phy_power(true);
+
+ tv_if_start();
+ }
+
+ /* video layer start */
+ if (s5ptv_status.vp_layer_enable)
+ tv_vlayer_start();
+
+ /* grp0 layer start */
+ if (s5ptv_status.grp_layer_enable[0])
+ tv_grp_start(VM_GPR0_LAYER);
+
+ /* grp1 layer start */
+ if (s5ptv_status.grp_layer_enable[1])
+ tv_grp_start(VM_GPR1_LAYER);
+
+ s5ptvfb_display_on(&s5ptv_status);
+ s5ptvfb_set_par(s5ptv_status.fb);
+
+ return 0;
+}
+#else
+#define s5p_tv_suspend NULL
+#define s5p_tv_resume NULL
+#endif
+
+static struct platform_driver s5p_tv_driver = {
+ .probe = s5p_tv_probe,
+ .remove = s5p_tv_remove,
+ .suspend = s5p_tv_suspend,
+ .resume = s5p_tv_resume,
+ .driver = {
+ .name = "s5p-tvout",
+ .owner = THIS_MODULE,
+ },
+};
+
+static char banner[] __initdata =
+ KERN_INFO "S5V210 TVOUT Driver, (c) 2010 Samsung Electronics\n";
+
+int __init s5p_tv_init(void)
+{
+ int ret;
+
+ printk(banner);
+
+ ret = platform_driver_register(&s5p_tv_driver);
+
+ if (ret) {
+ printk(KERN_ERR "Platform Device Register Failed %d\n", ret);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void __exit s5p_tv_exit(void)
+{
+ platform_driver_unregister(&s5p_tv_driver);
+}
+
+late_initcall(s5p_tv_init);
+module_exit(s5p_tv_exit);
+
+MODULE_AUTHOR("SangPil Moon");
+MODULE_DESCRIPTION("S5V210 TVOUT driver");
+MODULE_LICENSE("GPL");
« no previous file with comments | « drivers/media/video/samsung/tv20/s5p_tv.h ('k') | drivers/media/video/samsung/tv20/s5p_tv_v4l2.c » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698