249 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Generic MIPI DPI Panel Driver4 *5 * Copyright (C) 2013 Texas Instruments6 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>7 */8 9#include <linux/gpio/consumer.h>10#include <linux/module.h>11#include <linux/platform_device.h>12#include <linux/slab.h>13#include <linux/of.h>14 15#include <video/omapfb_dss.h>16#include <video/of_display_timing.h>17 18struct panel_drv_data {19 struct omap_dss_device dssdev;20 struct omap_dss_device *in;21 22 int data_lines;23 24 struct omap_video_timings videomode;25 26 struct gpio_desc *enable_gpio;27};28 29#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)30 31static int panel_dpi_connect(struct omap_dss_device *dssdev)32{33 struct panel_drv_data *ddata = to_panel_data(dssdev);34 struct omap_dss_device *in = ddata->in;35 36 if (omapdss_device_is_connected(dssdev))37 return 0;38 39 return in->ops.dpi->connect(in, dssdev);40}41 42static void panel_dpi_disconnect(struct omap_dss_device *dssdev)43{44 struct panel_drv_data *ddata = to_panel_data(dssdev);45 struct omap_dss_device *in = ddata->in;46 47 if (!omapdss_device_is_connected(dssdev))48 return;49 50 in->ops.dpi->disconnect(in, dssdev);51}52 53static int panel_dpi_enable(struct omap_dss_device *dssdev)54{55 struct panel_drv_data *ddata = to_panel_data(dssdev);56 struct omap_dss_device *in = ddata->in;57 int r;58 59 if (!omapdss_device_is_connected(dssdev))60 return -ENODEV;61 62 if (omapdss_device_is_enabled(dssdev))63 return 0;64 65 if (ddata->data_lines)66 in->ops.dpi->set_data_lines(in, ddata->data_lines);67 in->ops.dpi->set_timings(in, &ddata->videomode);68 69 r = in->ops.dpi->enable(in);70 if (r)71 return r;72 73 gpiod_set_value_cansleep(ddata->enable_gpio, 1);74 75 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;76 77 return 0;78}79 80static void panel_dpi_disable(struct omap_dss_device *dssdev)81{82 struct panel_drv_data *ddata = to_panel_data(dssdev);83 struct omap_dss_device *in = ddata->in;84 85 if (!omapdss_device_is_enabled(dssdev))86 return;87 88 gpiod_set_value_cansleep(ddata->enable_gpio, 0);89 90 in->ops.dpi->disable(in);91 92 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;93}94 95static void panel_dpi_set_timings(struct omap_dss_device *dssdev,96 struct omap_video_timings *timings)97{98 struct panel_drv_data *ddata = to_panel_data(dssdev);99 struct omap_dss_device *in = ddata->in;100 101 ddata->videomode = *timings;102 dssdev->panel.timings = *timings;103 104 in->ops.dpi->set_timings(in, timings);105}106 107static void panel_dpi_get_timings(struct omap_dss_device *dssdev,108 struct omap_video_timings *timings)109{110 struct panel_drv_data *ddata = to_panel_data(dssdev);111 112 *timings = ddata->videomode;113}114 115static int panel_dpi_check_timings(struct omap_dss_device *dssdev,116 struct omap_video_timings *timings)117{118 struct panel_drv_data *ddata = to_panel_data(dssdev);119 struct omap_dss_device *in = ddata->in;120 121 return in->ops.dpi->check_timings(in, timings);122}123 124static struct omap_dss_driver panel_dpi_ops = {125 .connect = panel_dpi_connect,126 .disconnect = panel_dpi_disconnect,127 128 .enable = panel_dpi_enable,129 .disable = panel_dpi_disable,130 131 .set_timings = panel_dpi_set_timings,132 .get_timings = panel_dpi_get_timings,133 .check_timings = panel_dpi_check_timings,134 135 .get_resolution = omapdss_default_get_resolution,136};137 138static int panel_dpi_probe_of(struct platform_device *pdev)139{140 struct panel_drv_data *ddata = platform_get_drvdata(pdev);141 struct device_node *node = pdev->dev.of_node;142 struct omap_dss_device *in;143 int r;144 struct display_timing timing;145 struct videomode vm;146 struct gpio_desc *gpio;147 148 gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_OUT_LOW);149 if (IS_ERR(gpio))150 return PTR_ERR(gpio);151 152 ddata->enable_gpio = gpio;153 154 r = of_get_display_timing(node, "panel-timing", &timing);155 if (r) {156 dev_err(&pdev->dev, "failed to get video timing\n");157 return r;158 }159 160 videomode_from_timing(&timing, &vm);161 videomode_to_omap_video_timings(&vm, &ddata->videomode);162 163 in = omapdss_of_find_source_for_first_ep(node);164 if (IS_ERR(in)) {165 dev_err(&pdev->dev, "failed to find video source\n");166 return PTR_ERR(in);167 }168 169 ddata->in = in;170 171 return 0;172}173 174static int panel_dpi_probe(struct platform_device *pdev)175{176 struct panel_drv_data *ddata;177 struct omap_dss_device *dssdev;178 int r;179 180 if (!pdev->dev.of_node)181 return -ENODEV;182 183 ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);184 if (ddata == NULL)185 return -ENOMEM;186 187 platform_set_drvdata(pdev, ddata);188 189 r = panel_dpi_probe_of(pdev);190 if (r)191 return r;192 193 dssdev = &ddata->dssdev;194 dssdev->dev = &pdev->dev;195 dssdev->driver = &panel_dpi_ops;196 dssdev->type = OMAP_DISPLAY_TYPE_DPI;197 dssdev->owner = THIS_MODULE;198 dssdev->panel.timings = ddata->videomode;199 dssdev->phy.dpi.data_lines = ddata->data_lines;200 201 r = omapdss_register_display(dssdev);202 if (r) {203 dev_err(&pdev->dev, "Failed to register panel\n");204 goto err_reg;205 }206 207 return 0;208 209err_reg:210 omap_dss_put_device(ddata->in);211 return r;212}213 214static void panel_dpi_remove(struct platform_device *pdev)215{216 struct panel_drv_data *ddata = platform_get_drvdata(pdev);217 struct omap_dss_device *dssdev = &ddata->dssdev;218 struct omap_dss_device *in = ddata->in;219 220 omapdss_unregister_display(dssdev);221 222 panel_dpi_disable(dssdev);223 panel_dpi_disconnect(dssdev);224 225 omap_dss_put_device(in);226}227 228static const struct of_device_id panel_dpi_of_match[] = {229 { .compatible = "omapdss,panel-dpi", },230 {},231};232 233MODULE_DEVICE_TABLE(of, panel_dpi_of_match);234 235static struct platform_driver panel_dpi_driver = {236 .probe = panel_dpi_probe,237 .remove = panel_dpi_remove,238 .driver = {239 .name = "panel-dpi",240 .of_match_table = panel_dpi_of_match,241 },242};243 244module_platform_driver(panel_dpi_driver);245 246MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");247MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver");248MODULE_LICENSE("GPL");249