Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * TFP410 DPI-to-DVI encoder driver
0004  *
0005  * Copyright (C) 2013 Texas Instruments
0006  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
0007  */
0008 
0009 #include <linux/gpio.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/slab.h>
0013 #include <linux/of_gpio.h>
0014 
0015 #include <video/omapfb_dss.h>
0016 
0017 struct panel_drv_data {
0018     struct omap_dss_device dssdev;
0019     struct omap_dss_device *in;
0020 
0021     int pd_gpio;
0022     int data_lines;
0023 
0024     struct omap_video_timings timings;
0025 };
0026 
0027 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
0028 
0029 static int tfp410_connect(struct omap_dss_device *dssdev,
0030         struct omap_dss_device *dst)
0031 {
0032     struct panel_drv_data *ddata = to_panel_data(dssdev);
0033     struct omap_dss_device *in = ddata->in;
0034     int r;
0035 
0036     if (omapdss_device_is_connected(dssdev))
0037         return -EBUSY;
0038 
0039     r = in->ops.dpi->connect(in, dssdev);
0040     if (r)
0041         return r;
0042 
0043     dst->src = dssdev;
0044     dssdev->dst = dst;
0045 
0046     return 0;
0047 }
0048 
0049 static void tfp410_disconnect(struct omap_dss_device *dssdev,
0050         struct omap_dss_device *dst)
0051 {
0052     struct panel_drv_data *ddata = to_panel_data(dssdev);
0053     struct omap_dss_device *in = ddata->in;
0054 
0055     WARN_ON(!omapdss_device_is_connected(dssdev));
0056     if (!omapdss_device_is_connected(dssdev))
0057         return;
0058 
0059     WARN_ON(dst != dssdev->dst);
0060     if (dst != dssdev->dst)
0061         return;
0062 
0063     dst->src = NULL;
0064     dssdev->dst = NULL;
0065 
0066     in->ops.dpi->disconnect(in, &ddata->dssdev);
0067 }
0068 
0069 static int tfp410_enable(struct omap_dss_device *dssdev)
0070 {
0071     struct panel_drv_data *ddata = to_panel_data(dssdev);
0072     struct omap_dss_device *in = ddata->in;
0073     int r;
0074 
0075     if (!omapdss_device_is_connected(dssdev))
0076         return -ENODEV;
0077 
0078     if (omapdss_device_is_enabled(dssdev))
0079         return 0;
0080 
0081     in->ops.dpi->set_timings(in, &ddata->timings);
0082     if (ddata->data_lines)
0083         in->ops.dpi->set_data_lines(in, ddata->data_lines);
0084 
0085     r = in->ops.dpi->enable(in);
0086     if (r)
0087         return r;
0088 
0089     if (gpio_is_valid(ddata->pd_gpio))
0090         gpio_set_value_cansleep(ddata->pd_gpio, 1);
0091 
0092     dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
0093 
0094     return 0;
0095 }
0096 
0097 static void tfp410_disable(struct omap_dss_device *dssdev)
0098 {
0099     struct panel_drv_data *ddata = to_panel_data(dssdev);
0100     struct omap_dss_device *in = ddata->in;
0101 
0102     if (!omapdss_device_is_enabled(dssdev))
0103         return;
0104 
0105     if (gpio_is_valid(ddata->pd_gpio))
0106         gpio_set_value_cansleep(ddata->pd_gpio, 0);
0107 
0108     in->ops.dpi->disable(in);
0109 
0110     dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
0111 }
0112 
0113 static void tfp410_fix_timings(struct omap_video_timings *timings)
0114 {
0115     timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
0116     timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
0117     timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;
0118 }
0119 
0120 static void tfp410_set_timings(struct omap_dss_device *dssdev,
0121         struct omap_video_timings *timings)
0122 {
0123     struct panel_drv_data *ddata = to_panel_data(dssdev);
0124     struct omap_dss_device *in = ddata->in;
0125 
0126     tfp410_fix_timings(timings);
0127 
0128     ddata->timings = *timings;
0129     dssdev->panel.timings = *timings;
0130 
0131     in->ops.dpi->set_timings(in, timings);
0132 }
0133 
0134 static void tfp410_get_timings(struct omap_dss_device *dssdev,
0135         struct omap_video_timings *timings)
0136 {
0137     struct panel_drv_data *ddata = to_panel_data(dssdev);
0138 
0139     *timings = ddata->timings;
0140 }
0141 
0142 static int tfp410_check_timings(struct omap_dss_device *dssdev,
0143         struct omap_video_timings *timings)
0144 {
0145     struct panel_drv_data *ddata = to_panel_data(dssdev);
0146     struct omap_dss_device *in = ddata->in;
0147 
0148     tfp410_fix_timings(timings);
0149 
0150     return in->ops.dpi->check_timings(in, timings);
0151 }
0152 
0153 static const struct omapdss_dvi_ops tfp410_dvi_ops = {
0154     .connect    = tfp410_connect,
0155     .disconnect = tfp410_disconnect,
0156 
0157     .enable     = tfp410_enable,
0158     .disable    = tfp410_disable,
0159 
0160     .check_timings  = tfp410_check_timings,
0161     .set_timings    = tfp410_set_timings,
0162     .get_timings    = tfp410_get_timings,
0163 };
0164 
0165 static int tfp410_probe_of(struct platform_device *pdev)
0166 {
0167     struct panel_drv_data *ddata = platform_get_drvdata(pdev);
0168     struct device_node *node = pdev->dev.of_node;
0169     struct omap_dss_device *in;
0170     int gpio;
0171 
0172     gpio = of_get_named_gpio(node, "powerdown-gpios", 0);
0173 
0174     if (gpio_is_valid(gpio) || gpio == -ENOENT) {
0175         ddata->pd_gpio = gpio;
0176     } else {
0177         dev_err(&pdev->dev, "failed to parse PD gpio\n");
0178         return gpio;
0179     }
0180 
0181     in = omapdss_of_find_source_for_first_ep(node);
0182     if (IS_ERR(in)) {
0183         dev_err(&pdev->dev, "failed to find video source\n");
0184         return PTR_ERR(in);
0185     }
0186 
0187     ddata->in = in;
0188 
0189     return 0;
0190 }
0191 
0192 static int tfp410_probe(struct platform_device *pdev)
0193 {
0194     struct panel_drv_data *ddata;
0195     struct omap_dss_device *dssdev;
0196     int r;
0197 
0198     if (!pdev->dev.of_node)
0199         return -ENODEV;
0200 
0201     ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
0202     if (!ddata)
0203         return -ENOMEM;
0204 
0205     platform_set_drvdata(pdev, ddata);
0206 
0207     r = tfp410_probe_of(pdev);
0208     if (r)
0209         return r;
0210 
0211     if (gpio_is_valid(ddata->pd_gpio)) {
0212         r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio,
0213                 GPIOF_OUT_INIT_LOW, "tfp410 PD");
0214         if (r) {
0215             dev_err(&pdev->dev, "Failed to request PD GPIO %d\n",
0216                     ddata->pd_gpio);
0217             goto err_gpio;
0218         }
0219     }
0220 
0221     dssdev = &ddata->dssdev;
0222     dssdev->ops.dvi = &tfp410_dvi_ops;
0223     dssdev->dev = &pdev->dev;
0224     dssdev->type = OMAP_DISPLAY_TYPE_DPI;
0225     dssdev->output_type = OMAP_DISPLAY_TYPE_DVI;
0226     dssdev->owner = THIS_MODULE;
0227     dssdev->phy.dpi.data_lines = ddata->data_lines;
0228     dssdev->port_num = 1;
0229 
0230     r = omapdss_register_output(dssdev);
0231     if (r) {
0232         dev_err(&pdev->dev, "Failed to register output\n");
0233         goto err_reg;
0234     }
0235 
0236     return 0;
0237 err_reg:
0238 err_gpio:
0239     omap_dss_put_device(ddata->in);
0240     return r;
0241 }
0242 
0243 static int __exit tfp410_remove(struct platform_device *pdev)
0244 {
0245     struct panel_drv_data *ddata = platform_get_drvdata(pdev);
0246     struct omap_dss_device *dssdev = &ddata->dssdev;
0247     struct omap_dss_device *in = ddata->in;
0248 
0249     omapdss_unregister_output(&ddata->dssdev);
0250 
0251     WARN_ON(omapdss_device_is_enabled(dssdev));
0252     if (omapdss_device_is_enabled(dssdev))
0253         tfp410_disable(dssdev);
0254 
0255     WARN_ON(omapdss_device_is_connected(dssdev));
0256     if (omapdss_device_is_connected(dssdev))
0257         tfp410_disconnect(dssdev, dssdev->dst);
0258 
0259     omap_dss_put_device(in);
0260 
0261     return 0;
0262 }
0263 
0264 static const struct of_device_id tfp410_of_match[] = {
0265     { .compatible = "omapdss,ti,tfp410", },
0266     {},
0267 };
0268 
0269 MODULE_DEVICE_TABLE(of, tfp410_of_match);
0270 
0271 static struct platform_driver tfp410_driver = {
0272     .probe  = tfp410_probe,
0273     .remove = __exit_p(tfp410_remove),
0274     .driver = {
0275         .name   = "tfp410",
0276         .of_match_table = tfp410_of_match,
0277         .suppress_bind_attrs = true,
0278     },
0279 };
0280 
0281 module_platform_driver(tfp410_driver);
0282 
0283 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
0284 MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");
0285 MODULE_LICENSE("GPL");