409 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2012 Texas Instruments4 * Author: Rob Clark <robdclark@gmail.com>5 */6 7#include <linux/backlight.h>8#include <linux/gpio/consumer.h>9#include <linux/platform_device.h>10 11#include <video/display_timing.h>12#include <video/of_display_timing.h>13#include <video/videomode.h>14 15#include <drm/drm_atomic_state_helper.h>16#include <drm/drm_connector.h>17#include <drm/drm_modeset_helper_vtables.h>18#include <drm/drm_probe_helper.h>19#include <drm/drm_simple_kms_helper.h>20 21#include "tilcdc_drv.h"22#include "tilcdc_panel.h"23 24struct panel_module {25 struct tilcdc_module base;26 struct tilcdc_panel_info *info;27 struct display_timings *timings;28 struct backlight_device *backlight;29 struct gpio_desc *enable_gpio;30};31#define to_panel_module(x) container_of(x, struct panel_module, base)32 33 34/*35 * Encoder:36 */37 38struct panel_encoder {39 struct drm_encoder base;40 struct panel_module *mod;41};42#define to_panel_encoder(x) container_of(x, struct panel_encoder, base)43 44static void panel_encoder_dpms(struct drm_encoder *encoder, int mode)45{46 struct panel_encoder *panel_encoder = to_panel_encoder(encoder);47 struct backlight_device *backlight = panel_encoder->mod->backlight;48 struct gpio_desc *gpio = panel_encoder->mod->enable_gpio;49 50 if (backlight) {51 backlight->props.power = mode == DRM_MODE_DPMS_ON ?52 BACKLIGHT_POWER_ON : BACKLIGHT_POWER_OFF;53 backlight_update_status(backlight);54 }55 56 if (gpio)57 gpiod_set_value_cansleep(gpio,58 mode == DRM_MODE_DPMS_ON ? 1 : 0);59}60 61static void panel_encoder_prepare(struct drm_encoder *encoder)62{63 panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);64}65 66static void panel_encoder_commit(struct drm_encoder *encoder)67{68 panel_encoder_dpms(encoder, DRM_MODE_DPMS_ON);69}70 71static void panel_encoder_mode_set(struct drm_encoder *encoder,72 struct drm_display_mode *mode,73 struct drm_display_mode *adjusted_mode)74{75 /* nothing needed */76}77 78static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = {79 .dpms = panel_encoder_dpms,80 .prepare = panel_encoder_prepare,81 .commit = panel_encoder_commit,82 .mode_set = panel_encoder_mode_set,83};84 85static struct drm_encoder *panel_encoder_create(struct drm_device *dev,86 struct panel_module *mod)87{88 struct panel_encoder *panel_encoder;89 struct drm_encoder *encoder;90 int ret;91 92 panel_encoder = devm_kzalloc(dev->dev, sizeof(*panel_encoder),93 GFP_KERNEL);94 if (!panel_encoder)95 return NULL;96 97 panel_encoder->mod = mod;98 99 encoder = &panel_encoder->base;100 encoder->possible_crtcs = 1;101 102 ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS);103 if (ret < 0)104 goto fail;105 106 drm_encoder_helper_add(encoder, &panel_encoder_helper_funcs);107 108 return encoder;109 110fail:111 drm_encoder_cleanup(encoder);112 return NULL;113}114 115/*116 * Connector:117 */118 119struct panel_connector {120 struct drm_connector base;121 122 struct drm_encoder *encoder; /* our connected encoder */123 struct panel_module *mod;124};125#define to_panel_connector(x) container_of(x, struct panel_connector, base)126 127 128static void panel_connector_destroy(struct drm_connector *connector)129{130 drm_connector_unregister(connector);131 drm_connector_cleanup(connector);132}133 134static int panel_connector_get_modes(struct drm_connector *connector)135{136 struct drm_device *dev = connector->dev;137 struct panel_connector *panel_connector = to_panel_connector(connector);138 struct display_timings *timings = panel_connector->mod->timings;139 int i;140 141 for (i = 0; i < timings->num_timings; i++) {142 struct drm_display_mode *mode;143 struct videomode vm;144 145 if (videomode_from_timings(timings, &vm, i))146 break;147 148 mode = drm_mode_create(dev);149 if (!mode)150 break;151 152 drm_display_mode_from_videomode(&vm, mode);153 154 mode->type = DRM_MODE_TYPE_DRIVER;155 156 if (timings->native_mode == i)157 mode->type |= DRM_MODE_TYPE_PREFERRED;158 159 drm_mode_set_name(mode);160 drm_mode_probed_add(connector, mode);161 }162 163 return i;164}165 166static struct drm_encoder *panel_connector_best_encoder(167 struct drm_connector *connector)168{169 struct panel_connector *panel_connector = to_panel_connector(connector);170 return panel_connector->encoder;171}172 173static const struct drm_connector_funcs panel_connector_funcs = {174 .destroy = panel_connector_destroy,175 .fill_modes = drm_helper_probe_single_connector_modes,176 .reset = drm_atomic_helper_connector_reset,177 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,178 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,179};180 181static const struct drm_connector_helper_funcs panel_connector_helper_funcs = {182 .get_modes = panel_connector_get_modes,183 .best_encoder = panel_connector_best_encoder,184};185 186static struct drm_connector *panel_connector_create(struct drm_device *dev,187 struct panel_module *mod, struct drm_encoder *encoder)188{189 struct panel_connector *panel_connector;190 struct drm_connector *connector;191 int ret;192 193 panel_connector = devm_kzalloc(dev->dev, sizeof(*panel_connector),194 GFP_KERNEL);195 if (!panel_connector)196 return NULL;197 198 panel_connector->encoder = encoder;199 panel_connector->mod = mod;200 201 connector = &panel_connector->base;202 203 drm_connector_init(dev, connector, &panel_connector_funcs,204 DRM_MODE_CONNECTOR_LVDS);205 drm_connector_helper_add(connector, &panel_connector_helper_funcs);206 207 connector->interlace_allowed = 0;208 connector->doublescan_allowed = 0;209 210 ret = drm_connector_attach_encoder(connector, encoder);211 if (ret)212 goto fail;213 214 return connector;215 216fail:217 panel_connector_destroy(connector);218 return NULL;219}220 221/*222 * Module:223 */224 225static int panel_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)226{227 struct panel_module *panel_mod = to_panel_module(mod);228 struct tilcdc_drm_private *priv = dev->dev_private;229 struct drm_encoder *encoder;230 struct drm_connector *connector;231 232 encoder = panel_encoder_create(dev, panel_mod);233 if (!encoder)234 return -ENOMEM;235 236 connector = panel_connector_create(dev, panel_mod, encoder);237 if (!connector)238 return -ENOMEM;239 240 priv->encoders[priv->num_encoders++] = encoder;241 priv->connectors[priv->num_connectors++] = connector;242 243 tilcdc_crtc_set_panel_info(priv->crtc,244 to_panel_encoder(encoder)->mod->info);245 246 return 0;247}248 249static const struct tilcdc_module_ops panel_module_ops = {250 .modeset_init = panel_modeset_init,251};252 253/*254 * Device:255 */256 257/* maybe move this somewhere common if it is needed by other outputs? */258static struct tilcdc_panel_info *of_get_panel_info(struct device_node *np)259{260 struct device_node *info_np;261 struct tilcdc_panel_info *info;262 int ret = 0;263 264 if (!np) {265 pr_err("%s: no devicenode given\n", __func__);266 return NULL;267 }268 269 info_np = of_get_child_by_name(np, "panel-info");270 if (!info_np) {271 pr_err("%s: could not find panel-info node\n", __func__);272 return NULL;273 }274 275 info = kzalloc(sizeof(*info), GFP_KERNEL);276 if (!info)277 goto put_node;278 279 ret |= of_property_read_u32(info_np, "ac-bias", &info->ac_bias);280 ret |= of_property_read_u32(info_np, "ac-bias-intrpt", &info->ac_bias_intrpt);281 ret |= of_property_read_u32(info_np, "dma-burst-sz", &info->dma_burst_sz);282 ret |= of_property_read_u32(info_np, "bpp", &info->bpp);283 ret |= of_property_read_u32(info_np, "fdd", &info->fdd);284 ret |= of_property_read_u32(info_np, "sync-edge", &info->sync_edge);285 ret |= of_property_read_u32(info_np, "sync-ctrl", &info->sync_ctrl);286 ret |= of_property_read_u32(info_np, "raster-order", &info->raster_order);287 ret |= of_property_read_u32(info_np, "fifo-th", &info->fifo_th);288 289 /* optional: */290 info->tft_alt_mode = of_property_read_bool(info_np, "tft-alt-mode");291 info->invert_pxl_clk = of_property_read_bool(info_np, "invert-pxl-clk");292 293 if (ret) {294 pr_err("%s: error reading panel-info properties\n", __func__);295 kfree(info);296 info = NULL;297 }298 299put_node:300 of_node_put(info_np);301 return info;302}303 304static int panel_probe(struct platform_device *pdev)305{306 struct device_node *node = pdev->dev.of_node;307 struct backlight_device *backlight;308 struct panel_module *panel_mod;309 struct tilcdc_module *mod;310 int ret;311 312 /* bail out early if no DT data: */313 if (!node) {314 dev_err(&pdev->dev, "device-tree data is missing\n");315 return -ENXIO;316 }317 318 panel_mod = devm_kzalloc(&pdev->dev, sizeof(*panel_mod), GFP_KERNEL);319 if (!panel_mod)320 return -ENOMEM;321 322 backlight = devm_of_find_backlight(&pdev->dev);323 if (IS_ERR(backlight))324 return PTR_ERR(backlight);325 panel_mod->backlight = backlight;326 327 panel_mod->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",328 GPIOD_OUT_LOW);329 if (IS_ERR(panel_mod->enable_gpio)) {330 ret = PTR_ERR(panel_mod->enable_gpio);331 dev_err(&pdev->dev, "failed to request enable GPIO\n");332 goto fail_backlight;333 }334 335 if (panel_mod->enable_gpio)336 dev_info(&pdev->dev, "found enable GPIO\n");337 338 mod = &panel_mod->base;339 pdev->dev.platform_data = mod;340 341 tilcdc_module_init(mod, "panel", &panel_module_ops);342 343 panel_mod->timings = of_get_display_timings(node);344 if (!panel_mod->timings) {345 dev_err(&pdev->dev, "could not get panel timings\n");346 ret = -EINVAL;347 goto fail_free;348 }349 350 panel_mod->info = of_get_panel_info(node);351 if (!panel_mod->info) {352 dev_err(&pdev->dev, "could not get panel info\n");353 ret = -EINVAL;354 goto fail_timings;355 }356 357 return 0;358 359fail_timings:360 display_timings_release(panel_mod->timings);361 362fail_free:363 tilcdc_module_cleanup(mod);364 365fail_backlight:366 if (panel_mod->backlight)367 put_device(&panel_mod->backlight->dev);368 return ret;369}370 371static void panel_remove(struct platform_device *pdev)372{373 struct tilcdc_module *mod = dev_get_platdata(&pdev->dev);374 struct panel_module *panel_mod = to_panel_module(mod);375 struct backlight_device *backlight = panel_mod->backlight;376 377 if (backlight)378 put_device(&backlight->dev);379 380 display_timings_release(panel_mod->timings);381 382 tilcdc_module_cleanup(mod);383 kfree(panel_mod->info);384}385 386static const struct of_device_id panel_of_match[] = {387 { .compatible = "ti,tilcdc,panel", },388 { },389};390 391static struct platform_driver panel_driver = {392 .probe = panel_probe,393 .remove_new = panel_remove,394 .driver = {395 .name = "tilcdc-panel",396 .of_match_table = panel_of_match,397 },398};399 400int __init tilcdc_panel_init(void)401{402 return platform_driver_register(&panel_driver);403}404 405void __exit tilcdc_panel_fini(void)406{407 platform_driver_unregister(&panel_driver);408}409