260 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Analog TV Connector driver4 *5 * Copyright (C) 2013 Texas Instruments6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>7 */8 9#include <linux/slab.h>10#include <linux/module.h>11#include <linux/platform_device.h>12#include <linux/of.h>13 14#include <video/omapfb_dss.h>15 16struct panel_drv_data {17 struct omap_dss_device dssdev;18 struct omap_dss_device *in;19 20 struct device *dev;21 22 struct omap_video_timings timings;23 24 bool invert_polarity;25};26 27static const struct omap_video_timings tvc_pal_timings = {28 .x_res = 720,29 .y_res = 574,30 .pixelclock = 13500000,31 .hsw = 64,32 .hfp = 12,33 .hbp = 68,34 .vsw = 5,35 .vfp = 5,36 .vbp = 41,37 38 .interlace = true,39};40 41static const struct of_device_id tvc_of_match[];42 43#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev)44 45static int tvc_connect(struct omap_dss_device *dssdev)46{47 struct panel_drv_data *ddata = to_panel_data(dssdev);48 struct omap_dss_device *in = ddata->in;49 50 dev_dbg(ddata->dev, "connect\n");51 52 if (omapdss_device_is_connected(dssdev))53 return 0;54 55 return in->ops.atv->connect(in, dssdev);56}57 58static void tvc_disconnect(struct omap_dss_device *dssdev)59{60 struct panel_drv_data *ddata = to_panel_data(dssdev);61 struct omap_dss_device *in = ddata->in;62 63 dev_dbg(ddata->dev, "disconnect\n");64 65 if (!omapdss_device_is_connected(dssdev))66 return;67 68 in->ops.atv->disconnect(in, dssdev);69}70 71static int tvc_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 dev_dbg(ddata->dev, "enable\n");78 79 if (!omapdss_device_is_connected(dssdev))80 return -ENODEV;81 82 if (omapdss_device_is_enabled(dssdev))83 return 0;84 85 in->ops.atv->set_timings(in, &ddata->timings);86 87 if (!ddata->dev->of_node) {88 in->ops.atv->set_type(in, OMAP_DSS_VENC_TYPE_COMPOSITE);89 90 in->ops.atv->invert_vid_out_polarity(in,91 ddata->invert_polarity);92 }93 94 r = in->ops.atv->enable(in);95 if (r)96 return r;97 98 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;99 100 return r;101}102 103static void tvc_disable(struct omap_dss_device *dssdev)104{105 struct panel_drv_data *ddata = to_panel_data(dssdev);106 struct omap_dss_device *in = ddata->in;107 108 dev_dbg(ddata->dev, "disable\n");109 110 if (!omapdss_device_is_enabled(dssdev))111 return;112 113 in->ops.atv->disable(in);114 115 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;116}117 118static void tvc_set_timings(struct omap_dss_device *dssdev,119 struct omap_video_timings *timings)120{121 struct panel_drv_data *ddata = to_panel_data(dssdev);122 struct omap_dss_device *in = ddata->in;123 124 ddata->timings = *timings;125 dssdev->panel.timings = *timings;126 127 in->ops.atv->set_timings(in, timings);128}129 130static void tvc_get_timings(struct omap_dss_device *dssdev,131 struct omap_video_timings *timings)132{133 struct panel_drv_data *ddata = to_panel_data(dssdev);134 135 *timings = ddata->timings;136}137 138static int tvc_check_timings(struct omap_dss_device *dssdev,139 struct omap_video_timings *timings)140{141 struct panel_drv_data *ddata = to_panel_data(dssdev);142 struct omap_dss_device *in = ddata->in;143 144 return in->ops.atv->check_timings(in, timings);145}146 147static u32 tvc_get_wss(struct omap_dss_device *dssdev)148{149 struct panel_drv_data *ddata = to_panel_data(dssdev);150 struct omap_dss_device *in = ddata->in;151 152 return in->ops.atv->get_wss(in);153}154 155static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss)156{157 struct panel_drv_data *ddata = to_panel_data(dssdev);158 struct omap_dss_device *in = ddata->in;159 160 return in->ops.atv->set_wss(in, wss);161}162 163static struct omap_dss_driver tvc_driver = {164 .connect = tvc_connect,165 .disconnect = tvc_disconnect,166 167 .enable = tvc_enable,168 .disable = tvc_disable,169 170 .set_timings = tvc_set_timings,171 .get_timings = tvc_get_timings,172 .check_timings = tvc_check_timings,173 174 .get_resolution = omapdss_default_get_resolution,175 176 .get_wss = tvc_get_wss,177 .set_wss = tvc_set_wss,178};179 180static int tvc_probe(struct platform_device *pdev)181{182 struct panel_drv_data *ddata;183 struct omap_dss_device *dssdev;184 int r;185 186 if (!pdev->dev.of_node)187 return -ENODEV;188 189 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);190 if (!ddata)191 return -ENOMEM;192 193 platform_set_drvdata(pdev, ddata);194 ddata->dev = &pdev->dev;195 196 ddata->in = omapdss_of_find_source_for_first_ep(pdev->dev.of_node);197 r = PTR_ERR_OR_ZERO(ddata->in);198 if (r) {199 dev_err(&pdev->dev, "failed to find video source\n");200 return r;201 }202 203 ddata->timings = tvc_pal_timings;204 205 dssdev = &ddata->dssdev;206 dssdev->driver = &tvc_driver;207 dssdev->dev = &pdev->dev;208 dssdev->type = OMAP_DISPLAY_TYPE_VENC;209 dssdev->owner = THIS_MODULE;210 dssdev->panel.timings = tvc_pal_timings;211 212 r = omapdss_register_display(dssdev);213 if (r) {214 dev_err(&pdev->dev, "Failed to register panel\n");215 goto err_reg;216 }217 218 return 0;219err_reg:220 omap_dss_put_device(ddata->in);221 return r;222}223 224static void tvc_remove(struct platform_device *pdev)225{226 struct panel_drv_data *ddata = platform_get_drvdata(pdev);227 struct omap_dss_device *dssdev = &ddata->dssdev;228 struct omap_dss_device *in = ddata->in;229 230 omapdss_unregister_display(&ddata->dssdev);231 232 tvc_disable(dssdev);233 tvc_disconnect(dssdev);234 235 omap_dss_put_device(in);236}237 238static const struct of_device_id tvc_of_match[] = {239 { .compatible = "omapdss,svideo-connector", },240 { .compatible = "omapdss,composite-video-connector", },241 {},242};243 244MODULE_DEVICE_TABLE(of, tvc_of_match);245 246static struct platform_driver tvc_connector_driver = {247 .probe = tvc_probe,248 .remove = tvc_remove,249 .driver = {250 .name = "connector-analog-tv",251 .of_match_table = tvc_of_match,252 },253};254 255module_platform_driver(tvc_connector_driver);256 257MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");258MODULE_DESCRIPTION("Analog TV Connector driver");259MODULE_LICENSE("GPL");260