brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 5f2d1b6 Raw
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