Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * TPD12S015 HDMI ESD protection & level shifter chip driver
0004  *
0005  * Copyright (C) 2013 Texas Instruments
0006  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
0007  */
0008 
0009 #include <linux/completion.h>
0010 #include <linux/delay.h>
0011 #include <linux/module.h>
0012 #include <linux/mod_devicetable.h>
0013 #include <linux/slab.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/gpio/consumer.h>
0016 
0017 #include <video/omapfb_dss.h>
0018 
0019 struct panel_drv_data {
0020     struct omap_dss_device dssdev;
0021     struct omap_dss_device *in;
0022 
0023     struct gpio_desc *ct_cp_hpd_gpio;
0024     struct gpio_desc *ls_oe_gpio;
0025     struct gpio_desc *hpd_gpio;
0026 
0027     struct omap_video_timings timings;
0028 };
0029 
0030 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)
0031 
0032 static int tpd_connect(struct omap_dss_device *dssdev,
0033         struct omap_dss_device *dst)
0034 {
0035     struct panel_drv_data *ddata = to_panel_data(dssdev);
0036     struct omap_dss_device *in = ddata->in;
0037     int r;
0038 
0039     r = in->ops.hdmi->connect(in, dssdev);
0040     if (r)
0041         return r;
0042 
0043     dst->src = dssdev;
0044     dssdev->dst = dst;
0045 
0046     if (ddata->ct_cp_hpd_gpio) {
0047         gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1);
0048         /* DC-DC converter needs at max 300us to get to 90% of 5V */
0049         udelay(300);
0050     }
0051 
0052     return 0;
0053 }
0054 
0055 static void tpd_disconnect(struct omap_dss_device *dssdev,
0056         struct omap_dss_device *dst)
0057 {
0058     struct panel_drv_data *ddata = to_panel_data(dssdev);
0059     struct omap_dss_device *in = ddata->in;
0060 
0061     WARN_ON(dst != dssdev->dst);
0062 
0063     if (dst != dssdev->dst)
0064         return;
0065 
0066     gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0);
0067 
0068     dst->src = NULL;
0069     dssdev->dst = NULL;
0070 
0071     in->ops.hdmi->disconnect(in, &ddata->dssdev);
0072 }
0073 
0074 static int tpd_enable(struct omap_dss_device *dssdev)
0075 {
0076     struct panel_drv_data *ddata = to_panel_data(dssdev);
0077     struct omap_dss_device *in = ddata->in;
0078     int r;
0079 
0080     if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
0081         return 0;
0082 
0083     in->ops.hdmi->set_timings(in, &ddata->timings);
0084 
0085     r = in->ops.hdmi->enable(in);
0086     if (r)
0087         return r;
0088 
0089     dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
0090 
0091     return r;
0092 }
0093 
0094 static void tpd_disable(struct omap_dss_device *dssdev)
0095 {
0096     struct panel_drv_data *ddata = to_panel_data(dssdev);
0097     struct omap_dss_device *in = ddata->in;
0098 
0099     if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
0100         return;
0101 
0102     in->ops.hdmi->disable(in);
0103 
0104     dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
0105 }
0106 
0107 static void tpd_set_timings(struct omap_dss_device *dssdev,
0108         struct omap_video_timings *timings)
0109 {
0110     struct panel_drv_data *ddata = to_panel_data(dssdev);
0111     struct omap_dss_device *in = ddata->in;
0112 
0113     ddata->timings = *timings;
0114     dssdev->panel.timings = *timings;
0115 
0116     in->ops.hdmi->set_timings(in, timings);
0117 }
0118 
0119 static void tpd_get_timings(struct omap_dss_device *dssdev,
0120         struct omap_video_timings *timings)
0121 {
0122     struct panel_drv_data *ddata = to_panel_data(dssdev);
0123 
0124     *timings = ddata->timings;
0125 }
0126 
0127 static int tpd_check_timings(struct omap_dss_device *dssdev,
0128         struct omap_video_timings *timings)
0129 {
0130     struct panel_drv_data *ddata = to_panel_data(dssdev);
0131     struct omap_dss_device *in = ddata->in;
0132     int r;
0133 
0134     r = in->ops.hdmi->check_timings(in, timings);
0135 
0136     return r;
0137 }
0138 
0139 static int tpd_read_edid(struct omap_dss_device *dssdev,
0140         u8 *edid, int len)
0141 {
0142     struct panel_drv_data *ddata = to_panel_data(dssdev);
0143     struct omap_dss_device *in = ddata->in;
0144     int r;
0145 
0146     if (!gpiod_get_value_cansleep(ddata->hpd_gpio))
0147         return -ENODEV;
0148 
0149     gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1);
0150 
0151     r = in->ops.hdmi->read_edid(in, edid, len);
0152 
0153     gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0);
0154 
0155     return r;
0156 }
0157 
0158 static bool tpd_detect(struct omap_dss_device *dssdev)
0159 {
0160     struct panel_drv_data *ddata = to_panel_data(dssdev);
0161 
0162     return gpiod_get_value_cansleep(ddata->hpd_gpio);
0163 }
0164 
0165 static int tpd_set_infoframe(struct omap_dss_device *dssdev,
0166         const struct hdmi_avi_infoframe *avi)
0167 {
0168     struct panel_drv_data *ddata = to_panel_data(dssdev);
0169     struct omap_dss_device *in = ddata->in;
0170 
0171     return in->ops.hdmi->set_infoframe(in, avi);
0172 }
0173 
0174 static int tpd_set_hdmi_mode(struct omap_dss_device *dssdev,
0175         bool hdmi_mode)
0176 {
0177     struct panel_drv_data *ddata = to_panel_data(dssdev);
0178     struct omap_dss_device *in = ddata->in;
0179 
0180     return in->ops.hdmi->set_hdmi_mode(in, hdmi_mode);
0181 }
0182 
0183 static const struct omapdss_hdmi_ops tpd_hdmi_ops = {
0184     .connect        = tpd_connect,
0185     .disconnect     = tpd_disconnect,
0186 
0187     .enable         = tpd_enable,
0188     .disable        = tpd_disable,
0189 
0190     .check_timings      = tpd_check_timings,
0191     .set_timings        = tpd_set_timings,
0192     .get_timings        = tpd_get_timings,
0193 
0194     .read_edid      = tpd_read_edid,
0195     .detect         = tpd_detect,
0196     .set_infoframe      = tpd_set_infoframe,
0197     .set_hdmi_mode      = tpd_set_hdmi_mode,
0198 };
0199 
0200 static int tpd_probe_of(struct platform_device *pdev)
0201 {
0202     struct panel_drv_data *ddata = platform_get_drvdata(pdev);
0203     struct device_node *node = pdev->dev.of_node;
0204     struct omap_dss_device *in;
0205 
0206     in = omapdss_of_find_source_for_first_ep(node);
0207     if (IS_ERR(in)) {
0208         dev_err(&pdev->dev, "failed to find video source\n");
0209         return PTR_ERR(in);
0210     }
0211 
0212     ddata->in = in;
0213 
0214     return 0;
0215 }
0216 
0217 static int tpd_probe(struct platform_device *pdev)
0218 {
0219     struct omap_dss_device *dssdev;
0220     struct panel_drv_data *ddata;
0221     int r;
0222     struct gpio_desc *gpio;
0223 
0224     ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
0225     if (!ddata)
0226         return -ENOMEM;
0227 
0228     platform_set_drvdata(pdev, ddata);
0229 
0230     if (pdev->dev.of_node) {
0231         r = tpd_probe_of(pdev);
0232         if (r)
0233             return r;
0234     } else {
0235         return -ENODEV;
0236     }
0237 
0238     gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0,
0239         GPIOD_OUT_LOW);
0240     if (IS_ERR(gpio)) {
0241         r = PTR_ERR(gpio);
0242         goto err_gpio;
0243     }
0244 
0245     ddata->ct_cp_hpd_gpio = gpio;
0246 
0247     gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1,
0248         GPIOD_OUT_LOW);
0249     if (IS_ERR(gpio)) {
0250         r = PTR_ERR(gpio);
0251         goto err_gpio;
0252     }
0253 
0254     ddata->ls_oe_gpio = gpio;
0255 
0256     gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2,
0257         GPIOD_IN);
0258     if (IS_ERR(gpio)) {
0259         r = PTR_ERR(gpio);
0260         goto err_gpio;
0261     }
0262 
0263     ddata->hpd_gpio = gpio;
0264 
0265     dssdev = &ddata->dssdev;
0266     dssdev->ops.hdmi = &tpd_hdmi_ops;
0267     dssdev->dev = &pdev->dev;
0268     dssdev->type = OMAP_DISPLAY_TYPE_HDMI;
0269     dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI;
0270     dssdev->owner = THIS_MODULE;
0271     dssdev->port_num = 1;
0272 
0273     r = omapdss_register_output(dssdev);
0274     if (r) {
0275         dev_err(&pdev->dev, "Failed to register output\n");
0276         goto err_reg;
0277     }
0278 
0279     return 0;
0280 err_reg:
0281 err_gpio:
0282     omap_dss_put_device(ddata->in);
0283     return r;
0284 }
0285 
0286 static int __exit tpd_remove(struct platform_device *pdev)
0287 {
0288     struct panel_drv_data *ddata = platform_get_drvdata(pdev);
0289     struct omap_dss_device *dssdev = &ddata->dssdev;
0290     struct omap_dss_device *in = ddata->in;
0291 
0292     omapdss_unregister_output(&ddata->dssdev);
0293 
0294     WARN_ON(omapdss_device_is_enabled(dssdev));
0295     if (omapdss_device_is_enabled(dssdev))
0296         tpd_disable(dssdev);
0297 
0298     WARN_ON(omapdss_device_is_connected(dssdev));
0299     if (omapdss_device_is_connected(dssdev))
0300         tpd_disconnect(dssdev, dssdev->dst);
0301 
0302     omap_dss_put_device(in);
0303 
0304     return 0;
0305 }
0306 
0307 static const struct of_device_id tpd_of_match[] = {
0308     { .compatible = "omapdss,ti,tpd12s015", },
0309     {},
0310 };
0311 
0312 MODULE_DEVICE_TABLE(of, tpd_of_match);
0313 
0314 static struct platform_driver tpd_driver = {
0315     .probe  = tpd_probe,
0316     .remove = __exit_p(tpd_remove),
0317     .driver = {
0318         .name   = "tpd12s015",
0319         .of_match_table = tpd_of_match,
0320         .suppress_bind_attrs = true,
0321     },
0322 };
0323 
0324 module_platform_driver(tpd_driver);
0325 
0326 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
0327 MODULE_DESCRIPTION("TPD12S015 driver");
0328 MODULE_LICENSE("GPL");