0001
0002
0003
0004
0005
0006
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
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");