260 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * TFP410 DPI-to-DVI encoder driver4 *5 * Copyright (C) 2013 Texas Instruments6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>7 */8 9#include <linux/err.h>10#include <linux/gpio/consumer.h>11#include <linux/module.h>12#include <linux/mod_devicetable.h>13#include <linux/platform_device.h>14#include <linux/slab.h>15 16#include <video/omapfb_dss.h>17 18struct panel_drv_data {19 struct omap_dss_device dssdev;20 struct omap_dss_device *in;21 22 struct gpio_desc *pd_gpio;23 24 int data_lines;25 26 struct omap_video_timings timings;27};28 29#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)30 31static int tfp410_connect(struct omap_dss_device *dssdev,32 struct omap_dss_device *dst)33{34 struct panel_drv_data *ddata = to_panel_data(dssdev);35 struct omap_dss_device *in = ddata->in;36 int r;37 38 if (omapdss_device_is_connected(dssdev))39 return -EBUSY;40 41 r = in->ops.dpi->connect(in, dssdev);42 if (r)43 return r;44 45 dst->src = dssdev;46 dssdev->dst = dst;47 48 return 0;49}50 51static void tfp410_disconnect(struct omap_dss_device *dssdev,52 struct omap_dss_device *dst)53{54 struct panel_drv_data *ddata = to_panel_data(dssdev);55 struct omap_dss_device *in = ddata->in;56 57 WARN_ON(!omapdss_device_is_connected(dssdev));58 if (!omapdss_device_is_connected(dssdev))59 return;60 61 WARN_ON(dst != dssdev->dst);62 if (dst != dssdev->dst)63 return;64 65 dst->src = NULL;66 dssdev->dst = NULL;67 68 in->ops.dpi->disconnect(in, &ddata->dssdev);69}70 71static int tfp410_enable(struct omap_dss_device *dssdev)72{73 struct panel_drv_data *ddata = to_panel_data(dssdev);74 struct omap_dss_device *in = ddata->in;75 int r;76 77 if (!omapdss_device_is_connected(dssdev))78 return -ENODEV;79 80 if (omapdss_device_is_enabled(dssdev))81 return 0;82 83 in->ops.dpi->set_timings(in, &ddata->timings);84 if (ddata->data_lines)85 in->ops.dpi->set_data_lines(in, ddata->data_lines);86 87 r = in->ops.dpi->enable(in);88 if (r)89 return r;90 91 if (ddata->pd_gpio)92 gpiod_set_value_cansleep(ddata->pd_gpio, 0);93 94 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;95 96 return 0;97}98 99static void tfp410_disable(struct omap_dss_device *dssdev)100{101 struct panel_drv_data *ddata = to_panel_data(dssdev);102 struct omap_dss_device *in = ddata->in;103 104 if (!omapdss_device_is_enabled(dssdev))105 return;106 107 if (ddata->pd_gpio)108 gpiod_set_value_cansleep(ddata->pd_gpio, 1);109 110 in->ops.dpi->disable(in);111 112 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;113}114 115static void tfp410_fix_timings(struct omap_video_timings *timings)116{117 timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;118 timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;119 timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;120}121 122static void tfp410_set_timings(struct omap_dss_device *dssdev,123 struct omap_video_timings *timings)124{125 struct panel_drv_data *ddata = to_panel_data(dssdev);126 struct omap_dss_device *in = ddata->in;127 128 tfp410_fix_timings(timings);129 130 ddata->timings = *timings;131 dssdev->panel.timings = *timings;132 133 in->ops.dpi->set_timings(in, timings);134}135 136static void tfp410_get_timings(struct omap_dss_device *dssdev,137 struct omap_video_timings *timings)138{139 struct panel_drv_data *ddata = to_panel_data(dssdev);140 141 *timings = ddata->timings;142}143 144static int tfp410_check_timings(struct omap_dss_device *dssdev,145 struct omap_video_timings *timings)146{147 struct panel_drv_data *ddata = to_panel_data(dssdev);148 struct omap_dss_device *in = ddata->in;149 150 tfp410_fix_timings(timings);151 152 return in->ops.dpi->check_timings(in, timings);153}154 155static const struct omapdss_dvi_ops tfp410_dvi_ops = {156 .connect = tfp410_connect,157 .disconnect = tfp410_disconnect,158 159 .enable = tfp410_enable,160 .disable = tfp410_disable,161 162 .check_timings = tfp410_check_timings,163 .set_timings = tfp410_set_timings,164 .get_timings = tfp410_get_timings,165};166 167static int tfp410_probe(struct platform_device *pdev)168{169 struct panel_drv_data *ddata;170 struct omap_dss_device *dssdev;171 int r;172 173 if (!pdev->dev.of_node)174 return -ENODEV;175 176 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);177 if (!ddata)178 return -ENOMEM;179 180 platform_set_drvdata(pdev, ddata);181 182 ddata->pd_gpio = devm_gpiod_get_optional(&pdev->dev, "powerdown",183 GPIOD_OUT_HIGH);184 r = PTR_ERR_OR_ZERO(ddata->pd_gpio);185 if (r) {186 dev_err(&pdev->dev, "Failed to request PD GPIO: %d\n", r);187 return r;188 }189 190 gpiod_set_consumer_name(ddata->pd_gpio, "tfp410 PD");191 192 ddata->in = omapdss_of_find_source_for_first_ep(pdev->dev.of_node);193 r = PTR_ERR_OR_ZERO(ddata->in);194 if (r) {195 dev_err(&pdev->dev, "failed to find video source: %d\n", r);196 return r;197 }198 199 dssdev = &ddata->dssdev;200 dssdev->ops.dvi = &tfp410_dvi_ops;201 dssdev->dev = &pdev->dev;202 dssdev->type = OMAP_DISPLAY_TYPE_DPI;203 dssdev->output_type = OMAP_DISPLAY_TYPE_DVI;204 dssdev->owner = THIS_MODULE;205 dssdev->phy.dpi.data_lines = ddata->data_lines;206 dssdev->port_num = 1;207 208 r = omapdss_register_output(dssdev);209 if (r) {210 dev_err(&pdev->dev, "Failed to register output\n");211 goto err_reg;212 }213 214 return 0;215err_reg:216 omap_dss_put_device(ddata->in);217 return r;218}219 220static void tfp410_remove(struct platform_device *pdev)221{222 struct panel_drv_data *ddata = platform_get_drvdata(pdev);223 struct omap_dss_device *dssdev = &ddata->dssdev;224 struct omap_dss_device *in = ddata->in;225 226 omapdss_unregister_output(&ddata->dssdev);227 228 WARN_ON(omapdss_device_is_enabled(dssdev));229 if (omapdss_device_is_enabled(dssdev))230 tfp410_disable(dssdev);231 232 WARN_ON(omapdss_device_is_connected(dssdev));233 if (omapdss_device_is_connected(dssdev))234 tfp410_disconnect(dssdev, dssdev->dst);235 236 omap_dss_put_device(in);237}238 239static const struct of_device_id tfp410_of_match[] = {240 { .compatible = "omapdss,ti,tfp410", },241 {},242};243 244MODULE_DEVICE_TABLE(of, tfp410_of_match);245 246static struct platform_driver tfp410_driver = {247 .probe = tfp410_probe,248 .remove = tfp410_remove,249 .driver = {250 .name = "tfp410",251 .of_match_table = tfp410_of_match,252 },253};254 255module_platform_driver(tfp410_driver);256 257MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");258MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver");259MODULE_LICENSE("GPL");260