1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * OPA362 analog video amplifier with output/power control 4 * 5 * Copyright (C) 2014 Golden Delicious Computers 6 * Author: H. Nikolaus Schaller <hns@goldelico.com> 7 * 8 * based on encoder-tfp410 9 * 10 * Copyright (C) 2013 Texas Instruments 11 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 12 */ 13 14 #include <linux/gpio/consumer.h> 15 #include <linux/module.h> 16 #include <linux/platform_device.h> 17 #include <linux/slab.h> 18 19 #include <video/omapfb_dss.h> 20 21 struct panel_drv_data { 22 struct omap_dss_device dssdev; 23 struct omap_dss_device *in; 24 25 struct gpio_desc *enable_gpio; 26 27 struct omap_video_timings timings; 28 }; 29 30 #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) 31 32 static int opa362_connect(struct omap_dss_device *dssdev, 33 struct omap_dss_device *dst) 34 { 35 struct panel_drv_data *ddata = to_panel_data(dssdev); 36 struct omap_dss_device *in = ddata->in; 37 int r; 38 39 dev_dbg(dssdev->dev, "connect\n"); 40 41 if (omapdss_device_is_connected(dssdev)) 42 return -EBUSY; 43 44 r = in->ops.atv->connect(in, dssdev); 45 if (r) 46 return r; 47 48 dst->src = dssdev; 49 dssdev->dst = dst; 50 51 return 0; 52 } 53 54 static void opa362_disconnect(struct omap_dss_device *dssdev, 55 struct omap_dss_device *dst) 56 { 57 struct panel_drv_data *ddata = to_panel_data(dssdev); 58 struct omap_dss_device *in = ddata->in; 59 60 dev_dbg(dssdev->dev, "disconnect\n"); 61 62 WARN_ON(!omapdss_device_is_connected(dssdev)); 63 if (!omapdss_device_is_connected(dssdev)) 64 return; 65 66 WARN_ON(dst != dssdev->dst); 67 if (dst != dssdev->dst) 68 return; 69 70 dst->src = NULL; 71 dssdev->dst = NULL; 72 73 in->ops.atv->disconnect(in, &ddata->dssdev); 74 } 75 76 static int opa362_enable(struct omap_dss_device *dssdev) 77 { 78 struct panel_drv_data *ddata = to_panel_data(dssdev); 79 struct omap_dss_device *in = ddata->in; 80 int r; 81 82 dev_dbg(dssdev->dev, "enable\n"); 83 84 if (!omapdss_device_is_connected(dssdev)) 85 return -ENODEV; 86 87 if (omapdss_device_is_enabled(dssdev)) 88 return 0; 89 90 in->ops.atv->set_timings(in, &ddata->timings); 91 92 r = in->ops.atv->enable(in); 93 if (r) 94 return r; 95 96 if (ddata->enable_gpio) 97 gpiod_set_value_cansleep(ddata->enable_gpio, 1); 98 99 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 100 101 return 0; 102 } 103 104 static void opa362_disable(struct omap_dss_device *dssdev) 105 { 106 struct panel_drv_data *ddata = to_panel_data(dssdev); 107 struct omap_dss_device *in = ddata->in; 108 109 dev_dbg(dssdev->dev, "disable\n"); 110 111 if (!omapdss_device_is_enabled(dssdev)) 112 return; 113 114 if (ddata->enable_gpio) 115 gpiod_set_value_cansleep(ddata->enable_gpio, 0); 116 117 in->ops.atv->disable(in); 118 119 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 120 } 121 122 static void opa362_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 dev_dbg(dssdev->dev, "set_timings\n"); 129 130 ddata->timings = *timings; 131 dssdev->panel.timings = *timings; 132 133 in->ops.atv->set_timings(in, timings); 134 } 135 136 static void opa362_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 dev_dbg(dssdev->dev, "get_timings\n"); 142 143 *timings = ddata->timings; 144 } 145 146 static int opa362_check_timings(struct omap_dss_device *dssdev, 147 struct omap_video_timings *timings) 148 { 149 struct panel_drv_data *ddata = to_panel_data(dssdev); 150 struct omap_dss_device *in = ddata->in; 151 152 dev_dbg(dssdev->dev, "check_timings\n"); 153 154 return in->ops.atv->check_timings(in, timings); 155 } 156 157 static void opa362_set_type(struct omap_dss_device *dssdev, 158 enum omap_dss_venc_type type) 159 { 160 /* we can only drive a COMPOSITE output */ 161 WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE); 162 163 } 164 165 static const struct omapdss_atv_ops opa362_atv_ops = { 166 .connect = opa362_connect, 167 .disconnect = opa362_disconnect, 168 169 .enable = opa362_enable, 170 .disable = opa362_disable, 171 172 .check_timings = opa362_check_timings, 173 .set_timings = opa362_set_timings, 174 .get_timings = opa362_get_timings, 175 176 .set_type = opa362_set_type, 177 }; 178 179 static int opa362_probe(struct platform_device *pdev) 180 { 181 struct device_node *node = pdev->dev.of_node; 182 struct panel_drv_data *ddata; 183 struct omap_dss_device *dssdev, *in; 184 struct gpio_desc *gpio; 185 int r; 186 187 dev_dbg(&pdev->dev, "probe\n"); 188 189 if (node == NULL) { 190 dev_err(&pdev->dev, "Unable to find device tree\n"); 191 return -EINVAL; 192 } 193 194 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 195 if (!ddata) 196 return -ENOMEM; 197 198 platform_set_drvdata(pdev, ddata); 199 200 gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW); 201 if (IS_ERR(gpio)) 202 return PTR_ERR(gpio); 203 204 ddata->enable_gpio = gpio; 205 206 in = omapdss_of_find_source_for_first_ep(node); 207 if (IS_ERR(in)) { 208 dev_err(&pdev->dev, "failed to find video source\n"); 209 return PTR_ERR(in); 210 } 211 212 ddata->in = in; 213 214 dssdev = &ddata->dssdev; 215 dssdev->ops.atv = &opa362_atv_ops; 216 dssdev->dev = &pdev->dev; 217 dssdev->type = OMAP_DISPLAY_TYPE_VENC; 218 dssdev->output_type = OMAP_DISPLAY_TYPE_VENC; 219 dssdev->owner = THIS_MODULE; 220 221 r = omapdss_register_output(dssdev); 222 if (r) { 223 dev_err(&pdev->dev, "Failed to register output\n"); 224 goto err_reg; 225 } 226 227 return 0; 228 err_reg: 229 omap_dss_put_device(ddata->in); 230 return r; 231 } 232 233 static void opa362_remove(struct platform_device *pdev) 234 { 235 struct panel_drv_data *ddata = platform_get_drvdata(pdev); 236 struct omap_dss_device *dssdev = &ddata->dssdev; 237 struct omap_dss_device *in = ddata->in; 238 239 omapdss_unregister_output(&ddata->dssdev); 240 241 WARN_ON(omapdss_device_is_enabled(dssdev)); 242 if (omapdss_device_is_enabled(dssdev)) 243 opa362_disable(dssdev); 244 245 WARN_ON(omapdss_device_is_connected(dssdev)); 246 if (omapdss_device_is_connected(dssdev)) 247 opa362_disconnect(dssdev, dssdev->dst); 248 249 omap_dss_put_device(in); 250 } 251 252 static const struct of_device_id opa362_of_match[] = { 253 { .compatible = "omapdss,ti,opa362", }, 254 {}, 255 }; 256 MODULE_DEVICE_TABLE(of, opa362_of_match); 257 258 static struct platform_driver opa362_driver = { 259 .probe = opa362_probe, 260 .remove = opa362_remove, 261 .driver = { 262 .name = "amplifier-opa362", 263 .of_match_table = opa362_of_match, 264 }, 265 }; 266 267 module_platform_driver(opa362_driver); 268 269 MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); 270 MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control"); 271 MODULE_LICENSE("GPL v2"); 272