Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 225 lines 5.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020 BayLibre, SAS 4 * Author: Neil Armstrong <narmstrong@baylibre.com> 5 */ 6 7#include <linux/delay.h> 8#include <linux/gpio/consumer.h> 9#include <linux/module.h> 10#include <linux/of.h> 11#include <linux/regulator/consumer.h> 12 13#include <video/mipi_display.h> 14 15#include <drm/drm_crtc.h> 16#include <drm/drm_device.h> 17#include <drm/drm_mipi_dsi.h> 18#include <drm/drm_modes.h> 19#include <drm/drm_panel.h> 20 21struct tdo_tl070wsh30_panel { 22 struct drm_panel base; 23 struct mipi_dsi_device *link; 24 25 struct regulator *supply; 26 struct gpio_desc *reset_gpio; 27}; 28 29static inline 30struct tdo_tl070wsh30_panel *to_tdo_tl070wsh30_panel(struct drm_panel *panel) 31{ 32 return container_of(panel, struct tdo_tl070wsh30_panel, base); 33} 34 35static int tdo_tl070wsh30_panel_prepare(struct drm_panel *panel) 36{ 37 struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel); 38 int err; 39 40 err = regulator_enable(tdo_tl070wsh30->supply); 41 if (err < 0) 42 return err; 43 44 usleep_range(10000, 11000); 45 46 gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 1); 47 48 usleep_range(10000, 11000); 49 50 gpiod_set_value_cansleep(tdo_tl070wsh30->reset_gpio, 0); 51 52 msleep(200); 53 54 err = mipi_dsi_dcs_exit_sleep_mode(tdo_tl070wsh30->link); 55 if (err < 0) { 56 dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); 57 regulator_disable(tdo_tl070wsh30->supply); 58 return err; 59 } 60 61 msleep(200); 62 63 err = mipi_dsi_dcs_set_display_on(tdo_tl070wsh30->link); 64 if (err < 0) { 65 dev_err(panel->dev, "failed to set display on: %d\n", err); 66 regulator_disable(tdo_tl070wsh30->supply); 67 return err; 68 } 69 70 msleep(20); 71 72 return 0; 73} 74 75static int tdo_tl070wsh30_panel_unprepare(struct drm_panel *panel) 76{ 77 struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = to_tdo_tl070wsh30_panel(panel); 78 int err; 79 80 err = mipi_dsi_dcs_set_display_off(tdo_tl070wsh30->link); 81 if (err < 0) 82 dev_err(panel->dev, "failed to set display off: %d\n", err); 83 84 usleep_range(10000, 11000); 85 86 err = mipi_dsi_dcs_enter_sleep_mode(tdo_tl070wsh30->link); 87 if (err < 0) { 88 dev_err(panel->dev, "failed to enter sleep mode: %d\n", err); 89 return err; 90 } 91 92 usleep_range(10000, 11000); 93 94 regulator_disable(tdo_tl070wsh30->supply); 95 96 return 0; 97} 98 99static const struct drm_display_mode default_mode = { 100 .clock = 47250, 101 .hdisplay = 1024, 102 .hsync_start = 1024 + 46, 103 .hsync_end = 1024 + 46 + 80, 104 .htotal = 1024 + 46 + 80 + 100, 105 .vdisplay = 600, 106 .vsync_start = 600 + 5, 107 .vsync_end = 600 + 5 + 5, 108 .vtotal = 600 + 5 + 5 + 20, 109 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, 110}; 111 112static int tdo_tl070wsh30_panel_get_modes(struct drm_panel *panel, 113 struct drm_connector *connector) 114{ 115 struct drm_display_mode *mode; 116 117 mode = drm_mode_duplicate(connector->dev, &default_mode); 118 if (!mode) { 119 dev_err(panel->dev, "failed to add mode %ux%u@%u\n", 120 default_mode.hdisplay, default_mode.vdisplay, 121 drm_mode_vrefresh(&default_mode)); 122 return -ENOMEM; 123 } 124 125 drm_mode_set_name(mode); 126 127 drm_mode_probed_add(connector, mode); 128 129 connector->display_info.width_mm = 154; 130 connector->display_info.height_mm = 85; 131 connector->display_info.bpc = 8; 132 133 return 1; 134} 135 136static const struct drm_panel_funcs tdo_tl070wsh30_panel_funcs = { 137 .unprepare = tdo_tl070wsh30_panel_unprepare, 138 .prepare = tdo_tl070wsh30_panel_prepare, 139 .get_modes = tdo_tl070wsh30_panel_get_modes, 140}; 141 142static const struct of_device_id tdo_tl070wsh30_of_match[] = { 143 { .compatible = "tdo,tl070wsh30", }, 144 { /* sentinel */ } 145}; 146MODULE_DEVICE_TABLE(of, tdo_tl070wsh30_of_match); 147 148static int tdo_tl070wsh30_panel_add(struct tdo_tl070wsh30_panel *tdo_tl070wsh30) 149{ 150 struct device *dev = &tdo_tl070wsh30->link->dev; 151 int err; 152 153 tdo_tl070wsh30->supply = devm_regulator_get(dev, "power"); 154 if (IS_ERR(tdo_tl070wsh30->supply)) 155 return PTR_ERR(tdo_tl070wsh30->supply); 156 157 tdo_tl070wsh30->reset_gpio = devm_gpiod_get(dev, "reset", 158 GPIOD_OUT_LOW); 159 if (IS_ERR(tdo_tl070wsh30->reset_gpio)) { 160 err = PTR_ERR(tdo_tl070wsh30->reset_gpio); 161 dev_dbg(dev, "failed to get reset gpio: %d\n", err); 162 return err; 163 } 164 165 drm_panel_init(&tdo_tl070wsh30->base, &tdo_tl070wsh30->link->dev, 166 &tdo_tl070wsh30_panel_funcs, DRM_MODE_CONNECTOR_DSI); 167 168 err = drm_panel_of_backlight(&tdo_tl070wsh30->base); 169 if (err) 170 return err; 171 172 drm_panel_add(&tdo_tl070wsh30->base); 173 174 return 0; 175} 176 177static int tdo_tl070wsh30_panel_probe(struct mipi_dsi_device *dsi) 178{ 179 struct tdo_tl070wsh30_panel *tdo_tl070wsh30; 180 int err; 181 182 dsi->lanes = 4; 183 dsi->format = MIPI_DSI_FMT_RGB888; 184 dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_LPM; 185 186 tdo_tl070wsh30 = devm_kzalloc(&dsi->dev, sizeof(*tdo_tl070wsh30), 187 GFP_KERNEL); 188 if (!tdo_tl070wsh30) 189 return -ENOMEM; 190 191 mipi_dsi_set_drvdata(dsi, tdo_tl070wsh30); 192 tdo_tl070wsh30->link = dsi; 193 194 err = tdo_tl070wsh30_panel_add(tdo_tl070wsh30); 195 if (err < 0) 196 return err; 197 198 return mipi_dsi_attach(dsi); 199} 200 201static void tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi) 202{ 203 struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi); 204 int err; 205 206 err = mipi_dsi_detach(dsi); 207 if (err < 0) 208 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); 209 210 drm_panel_remove(&tdo_tl070wsh30->base); 211} 212 213static struct mipi_dsi_driver tdo_tl070wsh30_panel_driver = { 214 .driver = { 215 .name = "panel-tdo-tl070wsh30", 216 .of_match_table = tdo_tl070wsh30_of_match, 217 }, 218 .probe = tdo_tl070wsh30_panel_probe, 219 .remove = tdo_tl070wsh30_panel_remove, 220}; 221module_mipi_dsi_driver(tdo_tl070wsh30_panel_driver); 222 223MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 224MODULE_DESCRIPTION("TDO TL070WSH30 panel driver"); 225MODULE_LICENSE("GPL v2");