1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2021 Linaro Ltd. |
3 | * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree: |
4 | * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. |
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_mipi_dsi.h> |
16 | #include <drm/drm_modes.h> |
17 | #include <drm/drm_panel.h> |
18 | |
19 | struct sharp_ls060 { |
20 | struct drm_panel panel; |
21 | struct mipi_dsi_device *dsi; |
22 | struct regulator *vddi_supply; |
23 | struct regulator *vddh_supply; |
24 | struct regulator *avdd_supply; |
25 | struct regulator *avee_supply; |
26 | struct gpio_desc *reset_gpio; |
27 | }; |
28 | |
29 | static inline struct sharp_ls060 *to_sharp_ls060(struct drm_panel *panel) |
30 | { |
31 | return container_of(panel, struct sharp_ls060, panel); |
32 | } |
33 | |
34 | static void sharp_ls060_reset(struct sharp_ls060 *ctx) |
35 | { |
36 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0); |
37 | usleep_range(min: 10000, max: 11000); |
38 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1); |
39 | usleep_range(min: 10000, max: 11000); |
40 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0); |
41 | usleep_range(min: 10000, max: 11000); |
42 | } |
43 | |
44 | static int sharp_ls060_on(struct sharp_ls060 *ctx) |
45 | { |
46 | struct mipi_dsi_device *dsi = ctx->dsi; |
47 | struct device *dev = &dsi->dev; |
48 | int ret; |
49 | |
50 | dsi->mode_flags |= MIPI_DSI_MODE_LPM; |
51 | |
52 | mipi_dsi_dcs_write_seq(dsi, 0xbb, 0x13); |
53 | mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_MEMORY_START); |
54 | |
55 | ret = mipi_dsi_dcs_exit_sleep_mode(dsi); |
56 | if (ret < 0) { |
57 | dev_err(dev, "Failed to exit sleep mode: %d\n" , ret); |
58 | return ret; |
59 | } |
60 | msleep(msecs: 120); |
61 | |
62 | ret = mipi_dsi_dcs_set_display_on(dsi); |
63 | if (ret < 0) { |
64 | dev_err(dev, "Failed to set display on: %d\n" , ret); |
65 | return ret; |
66 | } |
67 | msleep(msecs: 50); |
68 | |
69 | return 0; |
70 | } |
71 | |
72 | static int sharp_ls060_off(struct sharp_ls060 *ctx) |
73 | { |
74 | struct mipi_dsi_device *dsi = ctx->dsi; |
75 | struct device *dev = &dsi->dev; |
76 | int ret; |
77 | |
78 | dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; |
79 | |
80 | ret = mipi_dsi_dcs_set_display_off(dsi); |
81 | if (ret < 0) { |
82 | dev_err(dev, "Failed to set display off: %d\n" , ret); |
83 | return ret; |
84 | } |
85 | usleep_range(min: 2000, max: 3000); |
86 | |
87 | ret = mipi_dsi_dcs_enter_sleep_mode(dsi); |
88 | if (ret < 0) { |
89 | dev_err(dev, "Failed to enter sleep mode: %d\n" , ret); |
90 | return ret; |
91 | } |
92 | msleep(msecs: 121); |
93 | |
94 | return 0; |
95 | } |
96 | |
97 | static int sharp_ls060_prepare(struct drm_panel *panel) |
98 | { |
99 | struct sharp_ls060 *ctx = to_sharp_ls060(panel); |
100 | struct device *dev = &ctx->dsi->dev; |
101 | int ret; |
102 | |
103 | ret = regulator_enable(regulator: ctx->vddi_supply); |
104 | if (ret < 0) |
105 | return ret; |
106 | |
107 | ret = regulator_enable(regulator: ctx->avdd_supply); |
108 | if (ret < 0) |
109 | goto err_avdd; |
110 | |
111 | usleep_range(min: 1000, max: 2000); |
112 | |
113 | ret = regulator_enable(regulator: ctx->avee_supply); |
114 | if (ret < 0) |
115 | goto err_avee; |
116 | |
117 | usleep_range(min: 10000, max: 11000); |
118 | |
119 | ret = regulator_enable(regulator: ctx->vddh_supply); |
120 | if (ret < 0) |
121 | goto err_vddh; |
122 | |
123 | usleep_range(min: 10000, max: 11000); |
124 | |
125 | sharp_ls060_reset(ctx); |
126 | |
127 | ret = sharp_ls060_on(ctx); |
128 | if (ret < 0) { |
129 | dev_err(dev, "Failed to initialize panel: %d\n" , ret); |
130 | goto err_on; |
131 | } |
132 | |
133 | return 0; |
134 | |
135 | err_on: |
136 | regulator_disable(regulator: ctx->vddh_supply); |
137 | |
138 | usleep_range(min: 10000, max: 11000); |
139 | |
140 | err_vddh: |
141 | regulator_disable(regulator: ctx->avee_supply); |
142 | |
143 | err_avee: |
144 | regulator_disable(regulator: ctx->avdd_supply); |
145 | |
146 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1); |
147 | |
148 | err_avdd: |
149 | regulator_disable(regulator: ctx->vddi_supply); |
150 | |
151 | return ret; |
152 | } |
153 | |
154 | static int sharp_ls060_unprepare(struct drm_panel *panel) |
155 | { |
156 | struct sharp_ls060 *ctx = to_sharp_ls060(panel); |
157 | struct device *dev = &ctx->dsi->dev; |
158 | int ret; |
159 | |
160 | ret = sharp_ls060_off(ctx); |
161 | if (ret < 0) |
162 | dev_err(dev, "Failed to un-initialize panel: %d\n" , ret); |
163 | |
164 | regulator_disable(regulator: ctx->vddh_supply); |
165 | |
166 | usleep_range(min: 10000, max: 11000); |
167 | |
168 | regulator_disable(regulator: ctx->avee_supply); |
169 | regulator_disable(regulator: ctx->avdd_supply); |
170 | |
171 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1); |
172 | |
173 | regulator_disable(regulator: ctx->vddi_supply); |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | static const struct drm_display_mode sharp_ls060_mode = { |
179 | .clock = (1080 + 96 + 16 + 64) * (1920 + 4 + 1 + 16) * 60 / 1000, |
180 | .hdisplay = 1080, |
181 | .hsync_start = 1080 + 96, |
182 | .hsync_end = 1080 + 96 + 16, |
183 | .htotal = 1080 + 96 + 16 + 64, |
184 | .vdisplay = 1920, |
185 | .vsync_start = 1920 + 4, |
186 | .vsync_end = 1920 + 4 + 1, |
187 | .vtotal = 1920 + 4 + 1 + 16, |
188 | .width_mm = 75, |
189 | .height_mm = 132, |
190 | }; |
191 | |
192 | static int sharp_ls060_get_modes(struct drm_panel *panel, |
193 | struct drm_connector *connector) |
194 | { |
195 | struct drm_display_mode *mode; |
196 | |
197 | mode = drm_mode_duplicate(dev: connector->dev, mode: &sharp_ls060_mode); |
198 | if (!mode) |
199 | return -ENOMEM; |
200 | |
201 | drm_mode_set_name(mode); |
202 | |
203 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; |
204 | connector->display_info.width_mm = mode->width_mm; |
205 | connector->display_info.height_mm = mode->height_mm; |
206 | drm_mode_probed_add(connector, mode); |
207 | |
208 | return 1; |
209 | } |
210 | |
211 | static const struct drm_panel_funcs sharp_ls060_panel_funcs = { |
212 | .prepare = sharp_ls060_prepare, |
213 | .unprepare = sharp_ls060_unprepare, |
214 | .get_modes = sharp_ls060_get_modes, |
215 | }; |
216 | |
217 | static int sharp_ls060_probe(struct mipi_dsi_device *dsi) |
218 | { |
219 | struct device *dev = &dsi->dev; |
220 | struct sharp_ls060 *ctx; |
221 | int ret; |
222 | |
223 | ctx = devm_kzalloc(dev, size: sizeof(*ctx), GFP_KERNEL); |
224 | if (!ctx) |
225 | return -ENOMEM; |
226 | |
227 | ctx->vddi_supply = devm_regulator_get(dev, id: "vddi" ); |
228 | if (IS_ERR(ptr: ctx->vddi_supply)) |
229 | return PTR_ERR(ptr: ctx->vddi_supply); |
230 | |
231 | ctx->vddh_supply = devm_regulator_get(dev, id: "vddh" ); |
232 | if (IS_ERR(ptr: ctx->vddh_supply)) |
233 | return PTR_ERR(ptr: ctx->vddh_supply); |
234 | |
235 | ctx->avdd_supply = devm_regulator_get(dev, id: "avdd" ); |
236 | if (IS_ERR(ptr: ctx->avdd_supply)) |
237 | return PTR_ERR(ptr: ctx->avdd_supply); |
238 | |
239 | ctx->avee_supply = devm_regulator_get(dev, id: "avee" ); |
240 | if (IS_ERR(ptr: ctx->avee_supply)) |
241 | return PTR_ERR(ptr: ctx->avee_supply); |
242 | |
243 | ctx->reset_gpio = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
244 | if (IS_ERR(ptr: ctx->reset_gpio)) |
245 | return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->reset_gpio), |
246 | fmt: "Failed to get reset-gpios\n" ); |
247 | |
248 | ctx->dsi = dsi; |
249 | mipi_dsi_set_drvdata(dsi, data: ctx); |
250 | |
251 | dsi->lanes = 4; |
252 | dsi->format = MIPI_DSI_FMT_RGB888; |
253 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | |
254 | MIPI_DSI_MODE_NO_EOT_PACKET | |
255 | MIPI_DSI_CLOCK_NON_CONTINUOUS; |
256 | |
257 | drm_panel_init(panel: &ctx->panel, dev, funcs: &sharp_ls060_panel_funcs, |
258 | DRM_MODE_CONNECTOR_DSI); |
259 | |
260 | ret = drm_panel_of_backlight(panel: &ctx->panel); |
261 | if (ret) |
262 | return dev_err_probe(dev, err: ret, fmt: "Failed to get backlight\n" ); |
263 | |
264 | drm_panel_add(panel: &ctx->panel); |
265 | |
266 | ret = mipi_dsi_attach(dsi); |
267 | if (ret < 0) { |
268 | dev_err(dev, "Failed to attach to DSI host: %d\n" , ret); |
269 | drm_panel_remove(panel: &ctx->panel); |
270 | return ret; |
271 | } |
272 | |
273 | return 0; |
274 | } |
275 | |
276 | static void sharp_ls060_remove(struct mipi_dsi_device *dsi) |
277 | { |
278 | struct sharp_ls060 *ctx = mipi_dsi_get_drvdata(dsi); |
279 | int ret; |
280 | |
281 | ret = mipi_dsi_detach(dsi); |
282 | if (ret < 0) |
283 | dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n" , ret); |
284 | |
285 | drm_panel_remove(panel: &ctx->panel); |
286 | } |
287 | |
288 | static const struct of_device_id sharp_ls060t1sx01_of_match[] = { |
289 | { .compatible = "sharp,ls060t1sx01" }, |
290 | { /* sentinel */ } |
291 | }; |
292 | MODULE_DEVICE_TABLE(of, sharp_ls060t1sx01_of_match); |
293 | |
294 | static struct mipi_dsi_driver sharp_ls060_driver = { |
295 | .probe = sharp_ls060_probe, |
296 | .remove = sharp_ls060_remove, |
297 | .driver = { |
298 | .name = "panel-sharp-ls060t1sx01" , |
299 | .of_match_table = sharp_ls060t1sx01_of_match, |
300 | }, |
301 | }; |
302 | module_mipi_dsi_driver(sharp_ls060_driver); |
303 | |
304 | MODULE_AUTHOR("Dmitry Baryshkov <dmitry.baryshkov@linaro.org>" ); |
305 | MODULE_DESCRIPTION("DRM driver for Sharp LS060T1SX01 1080p video mode dsi panel" ); |
306 | MODULE_LICENSE("GPL v2" ); |
307 | |