Loading...
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2016 InforceComputing
4 * Copyright (C) 2016 Linaro Ltd
5 * Copyright (C) 2023 BayLibre, SAS
6 *
7 * Authors:
8 * - Vinay Simha BN <simhavcs@gmail.com>
9 * - Sumit Semwal <sumit.semwal@linaro.org>
10 * - Guillaume La Roque <glaroque@baylibre.com>
11 *
12 */
13
14#include <linux/backlight.h>
15#include <linux/delay.h>
16#include <linux/gpio/consumer.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/regulator/consumer.h>
20
21#include <video/mipi_display.h>
22
23#include <drm/drm_mipi_dsi.h>
24#include <drm/drm_modes.h>
25#include <drm/drm_panel.h>
26
27#define DSI_REG_MCAP 0xb0
28#define DSI_REG_IS 0xb3 /* Interface Setting */
29#define DSI_REG_IIS 0xb4 /* Interface ID Setting */
30#define DSI_REG_CTRL 0xb6
31
32enum {
33 IOVCC = 0,
34 POWER = 1
35};
36
37struct stk_panel {
38 const struct drm_display_mode *mode;
39 struct backlight_device *backlight;
40 struct drm_panel base;
41 struct gpio_desc *enable_gpio; /* Power IC supply enable */
42 struct gpio_desc *reset_gpio; /* External reset */
43 struct mipi_dsi_device *dsi;
44 struct regulator_bulk_data supplies[2];
45};
46
47static inline struct stk_panel *to_stk_panel(struct drm_panel *panel)
48{
49 return container_of(panel, struct stk_panel, base);
50}
51
52static int stk_panel_init(struct stk_panel *stk)
53{
54 struct mipi_dsi_device *dsi = stk->dsi;
55 struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
56
57 mipi_dsi_dcs_soft_reset_multi(&dsi_ctx);
58 mipi_dsi_msleep(&dsi_ctx, 5);
59 mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
60 mipi_dsi_msleep(&dsi_ctx, 120);
61
62 mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_MCAP, 0x04);
63
64 /* Interface setting, video mode */
65 mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00);
66 mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_IIS, 0x0c, 0x00);
67 mipi_dsi_generic_write_seq_multi(&dsi_ctx, DSI_REG_CTRL, 0x3a, 0xd3);
68
69 mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0x77);
70
71 mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
72 MIPI_DCS_WRITE_MEMORY_START);
73
74 mipi_dsi_dcs_set_pixel_format_multi(&dsi_ctx, 0x77);
75 mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, stk->mode->hdisplay - 1);
76 mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, stk->mode->vdisplay - 1);
77
78 return dsi_ctx.accum_err;
79}
80
81static int stk_panel_on(struct stk_panel *stk)
82{
83 struct mipi_dsi_device *dsi = stk->dsi;
84 struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
85
86 mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
87
88 mipi_dsi_msleep(&dsi_ctx, 20);
89
90 return dsi_ctx.accum_err;
91}
92
93static void stk_panel_off(struct stk_panel *stk)
94{
95 struct mipi_dsi_device *dsi = stk->dsi;
96 struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
97
98 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
99
100 mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
101 mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
102
103 mipi_dsi_msleep(&dsi_ctx, 100);
104}
105
106static int stk_panel_unprepare(struct drm_panel *panel)
107{
108 struct stk_panel *stk = to_stk_panel(panel);
109
110 stk_panel_off(stk);
111 regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies);
112 gpiod_set_value(stk->reset_gpio, 0);
113 gpiod_set_value(stk->enable_gpio, 1);
114
115 return 0;
116}
117
118static int stk_panel_prepare(struct drm_panel *panel)
119{
120 struct stk_panel *stk = to_stk_panel(panel);
121 int ret;
122
123 gpiod_set_value(stk->reset_gpio, 0);
124 gpiod_set_value(stk->enable_gpio, 0);
125 ret = regulator_enable(stk->supplies[IOVCC].consumer);
126 if (ret < 0)
127 return ret;
128
129 mdelay(8);
130 ret = regulator_enable(stk->supplies[POWER].consumer);
131 if (ret < 0)
132 goto iovccoff;
133
134 mdelay(20);
135 gpiod_set_value(stk->enable_gpio, 1);
136 mdelay(20);
137 gpiod_set_value(stk->reset_gpio, 1);
138 mdelay(10);
139 ret = stk_panel_init(stk);
140 if (ret < 0)
141 goto poweroff;
142
143 ret = stk_panel_on(stk);
144 if (ret < 0)
145 goto poweroff;
146
147 return 0;
148
149poweroff:
150 regulator_disable(stk->supplies[POWER].consumer);
151iovccoff:
152 regulator_disable(stk->supplies[IOVCC].consumer);
153 gpiod_set_value(stk->reset_gpio, 0);
154 gpiod_set_value(stk->enable_gpio, 0);
155
156 return ret;
157}
158
159static const struct drm_display_mode default_mode = {
160 .clock = 163204,
161 .hdisplay = 1200,
162 .hsync_start = 1200 + 144,
163 .hsync_end = 1200 + 144 + 16,
164 .htotal = 1200 + 144 + 16 + 45,
165 .vdisplay = 1920,
166 .vsync_start = 1920 + 8,
167 .vsync_end = 1920 + 8 + 4,
168 .vtotal = 1920 + 8 + 4 + 4,
169 .width_mm = 95,
170 .height_mm = 151,
171};
172
173static int stk_panel_get_modes(struct drm_panel *panel,
174 struct drm_connector *connector)
175{
176 struct drm_display_mode *mode;
177
178 mode = drm_mode_duplicate(connector->dev, &default_mode);
179 if (!mode) {
180 dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
181 default_mode.hdisplay, default_mode.vdisplay,
182 drm_mode_vrefresh(&default_mode));
183 return -ENOMEM;
184 }
185
186 drm_mode_set_name(mode);
187 drm_mode_probed_add(connector, mode);
188 connector->display_info.width_mm = default_mode.width_mm;
189 connector->display_info.height_mm = default_mode.height_mm;
190 return 1;
191}
192
193static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)
194{
195 struct mipi_dsi_device *dsi = bl_get_data(bl);
196 int ret;
197 u16 brightness;
198
199 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
200 ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
201 if (ret < 0)
202 return ret;
203
204 dsi->mode_flags |= MIPI_DSI_MODE_LPM;
205 return brightness & 0xff;
206}
207
208static int dsi_dcs_bl_update_status(struct backlight_device *bl)
209{
210 struct mipi_dsi_device *dsi = bl_get_data(bl);
211 struct mipi_dsi_multi_context dsi_ctx = {.dsi = dsi};
212
213 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
214 mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, bl->props.brightness);
215 if (dsi_ctx.accum_err)
216 return dsi_ctx.accum_err;
217
218 dsi->mode_flags |= MIPI_DSI_MODE_LPM;
219 return dsi_ctx.accum_err;
220}
221
222static const struct backlight_ops dsi_bl_ops = {
223 .update_status = dsi_dcs_bl_update_status,
224 .get_brightness = dsi_dcs_bl_get_brightness,
225};
226
227static struct backlight_device *
228drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
229{
230 struct device *dev = &dsi->dev;
231 struct backlight_properties props = {
232 .type = BACKLIGHT_RAW,
233 .brightness = 255,
234 .max_brightness = 255,
235 };
236
237 return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
238 &dsi_bl_ops, &props);
239}
240
241static const struct drm_panel_funcs stk_panel_funcs = {
242 .unprepare = stk_panel_unprepare,
243 .prepare = stk_panel_prepare,
244 .get_modes = stk_panel_get_modes,
245};
246
247static const struct of_device_id stk_of_match[] = {
248 { .compatible = "startek,kd070fhfid015", },
249 { }
250};
251MODULE_DEVICE_TABLE(of, stk_of_match);
252
253static int stk_panel_add(struct stk_panel *stk)
254{
255 struct device *dev = &stk->dsi->dev;
256 int ret;
257
258 stk->mode = &default_mode;
259
260 stk->supplies[IOVCC].supply = "iovcc";
261 stk->supplies[POWER].supply = "power";
262 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies);
263 if (ret) {
264 dev_err(dev, "regulator_bulk failed\n");
265 return ret;
266 }
267
268 stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
269 if (IS_ERR(stk->reset_gpio)) {
270 ret = PTR_ERR(stk->reset_gpio);
271 dev_err(dev, "cannot get reset-gpios %d\n", ret);
272 return ret;
273 }
274
275 stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
276 if (IS_ERR(stk->enable_gpio)) {
277 ret = PTR_ERR(stk->enable_gpio);
278 dev_err(dev, "cannot get enable-gpio %d\n", ret);
279 return ret;
280 }
281
282 stk->backlight = drm_panel_create_dsi_backlight(stk->dsi);
283 if (IS_ERR(stk->backlight)) {
284 ret = PTR_ERR(stk->backlight);
285 dev_err(dev, "failed to register backlight %d\n", ret);
286 return ret;
287 }
288
289 drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs,
290 DRM_MODE_CONNECTOR_DSI);
291
292 drm_panel_add(&stk->base);
293
294 return 0;
295}
296
297static int stk_panel_probe(struct mipi_dsi_device *dsi)
298{
299 struct stk_panel *stk;
300 int ret;
301
302 dsi->lanes = 4;
303 dsi->format = MIPI_DSI_FMT_RGB888;
304 dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM);
305
306 stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL);
307 if (!stk)
308 return -ENOMEM;
309
310 mipi_dsi_set_drvdata(dsi, stk);
311
312 stk->dsi = dsi;
313
314 ret = stk_panel_add(stk);
315 if (ret < 0)
316 return ret;
317
318 ret = mipi_dsi_attach(dsi);
319 if (ret < 0)
320 drm_panel_remove(&stk->base);
321
322 return 0;
323}
324
325static void stk_panel_remove(struct mipi_dsi_device *dsi)
326{
327 struct stk_panel *stk = mipi_dsi_get_drvdata(dsi);
328 int err;
329
330 err = mipi_dsi_detach(dsi);
331 if (err < 0)
332 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
333 err);
334
335 drm_panel_remove(&stk->base);
336}
337
338static struct mipi_dsi_driver stk_panel_driver = {
339 .driver = {
340 .name = "panel-startek-kd070fhfid015",
341 .of_match_table = stk_of_match,
342 },
343 .probe = stk_panel_probe,
344 .remove = stk_panel_remove,
345};
346module_mipi_dsi_driver(stk_panel_driver);
347
348MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
349MODULE_DESCRIPTION("STARTEK KD070FHFID015");
350MODULE_LICENSE("GPL");
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2016 InforceComputing
4 * Copyright (C) 2016 Linaro Ltd
5 * Copyright (C) 2023 BayLibre, SAS
6 *
7 * Authors:
8 * - Vinay Simha BN <simhavcs@gmail.com>
9 * - Sumit Semwal <sumit.semwal@linaro.org>
10 * - Guillaume La Roque <glaroque@baylibre.com>
11 *
12 */
13
14#include <linux/backlight.h>
15#include <linux/delay.h>
16#include <linux/gpio/consumer.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/regulator/consumer.h>
20
21#include <video/mipi_display.h>
22
23#include <drm/drm_mipi_dsi.h>
24#include <drm/drm_modes.h>
25#include <drm/drm_panel.h>
26
27#define DSI_REG_MCAP 0xB0
28#define DSI_REG_IS 0xB3 /* Interface Setting */
29#define DSI_REG_IIS 0xB4 /* Interface ID Setting */
30#define DSI_REG_CTRL 0xB6
31
32enum {
33 IOVCC = 0,
34 POWER = 1
35};
36
37struct stk_panel {
38 const struct drm_display_mode *mode;
39 struct backlight_device *backlight;
40 struct drm_panel base;
41 struct gpio_desc *enable_gpio; /* Power IC supply enable */
42 struct gpio_desc *reset_gpio; /* External reset */
43 struct mipi_dsi_device *dsi;
44 struct regulator_bulk_data supplies[2];
45};
46
47static inline struct stk_panel *to_stk_panel(struct drm_panel *panel)
48{
49 return container_of(panel, struct stk_panel, base);
50}
51
52static int stk_panel_init(struct stk_panel *stk)
53{
54 struct mipi_dsi_device *dsi = stk->dsi;
55 struct device *dev = &stk->dsi->dev;
56 int ret;
57
58 ret = mipi_dsi_dcs_soft_reset(dsi);
59 if (ret < 0) {
60 dev_err(dev, "failed to mipi_dsi_dcs_soft_reset: %d\n", ret);
61 return ret;
62 }
63 mdelay(5);
64
65 ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
66 if (ret < 0) {
67 dev_err(dev, "failed to set exit sleep mode: %d\n", ret);
68 return ret;
69 }
70 msleep(120);
71
72 mipi_dsi_generic_write_seq(dsi, DSI_REG_MCAP, 0x04);
73
74 /* Interface setting, video mode */
75 mipi_dsi_generic_write_seq(dsi, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00);
76 mipi_dsi_generic_write_seq(dsi, DSI_REG_IIS, 0x0C, 0x00);
77 mipi_dsi_generic_write_seq(dsi, DSI_REG_CTRL, 0x3A, 0xD3);
78
79 ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x77);
80 if (ret < 0) {
81 dev_err(dev, "failed to write display brightness: %d\n", ret);
82 return ret;
83 }
84
85 mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
86 MIPI_DCS_WRITE_MEMORY_START);
87
88 ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77);
89 if (ret < 0) {
90 dev_err(dev, "failed to set pixel format: %d\n", ret);
91 return ret;
92 }
93
94 ret = mipi_dsi_dcs_set_column_address(dsi, 0, stk->mode->hdisplay - 1);
95 if (ret < 0) {
96 dev_err(dev, "failed to set column address: %d\n", ret);
97 return ret;
98 }
99
100 ret = mipi_dsi_dcs_set_page_address(dsi, 0, stk->mode->vdisplay - 1);
101 if (ret < 0) {
102 dev_err(dev, "failed to set page address: %d\n", ret);
103 return ret;
104 }
105
106 return 0;
107}
108
109static int stk_panel_on(struct stk_panel *stk)
110{
111 struct mipi_dsi_device *dsi = stk->dsi;
112 struct device *dev = &stk->dsi->dev;
113 int ret;
114
115 ret = mipi_dsi_dcs_set_display_on(dsi);
116 if (ret < 0)
117 dev_err(dev, "failed to set display on: %d\n", ret);
118
119 mdelay(20);
120
121 return ret;
122}
123
124static void stk_panel_off(struct stk_panel *stk)
125{
126 struct mipi_dsi_device *dsi = stk->dsi;
127 struct device *dev = &stk->dsi->dev;
128 int ret;
129
130 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
131
132 ret = mipi_dsi_dcs_set_display_off(dsi);
133 if (ret < 0)
134 dev_err(dev, "failed to set display off: %d\n", ret);
135
136 ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
137 if (ret < 0)
138 dev_err(dev, "failed to enter sleep mode: %d\n", ret);
139
140 msleep(100);
141}
142
143static int stk_panel_unprepare(struct drm_panel *panel)
144{
145 struct stk_panel *stk = to_stk_panel(panel);
146
147 stk_panel_off(stk);
148 regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies);
149 gpiod_set_value(stk->reset_gpio, 0);
150 gpiod_set_value(stk->enable_gpio, 1);
151
152 return 0;
153}
154
155static int stk_panel_prepare(struct drm_panel *panel)
156{
157 struct stk_panel *stk = to_stk_panel(panel);
158 struct device *dev = &stk->dsi->dev;
159 int ret;
160
161 gpiod_set_value(stk->reset_gpio, 0);
162 gpiod_set_value(stk->enable_gpio, 0);
163 ret = regulator_enable(stk->supplies[IOVCC].consumer);
164 if (ret < 0)
165 return ret;
166
167 mdelay(8);
168 ret = regulator_enable(stk->supplies[POWER].consumer);
169 if (ret < 0)
170 goto iovccoff;
171
172 mdelay(20);
173 gpiod_set_value(stk->enable_gpio, 1);
174 mdelay(20);
175 gpiod_set_value(stk->reset_gpio, 1);
176 mdelay(10);
177 ret = stk_panel_init(stk);
178 if (ret < 0) {
179 dev_err(dev, "failed to init panel: %d\n", ret);
180 goto poweroff;
181 }
182
183 ret = stk_panel_on(stk);
184 if (ret < 0) {
185 dev_err(dev, "failed to set panel on: %d\n", ret);
186 goto poweroff;
187 }
188
189 return 0;
190
191poweroff:
192 regulator_disable(stk->supplies[POWER].consumer);
193iovccoff:
194 regulator_disable(stk->supplies[IOVCC].consumer);
195 gpiod_set_value(stk->reset_gpio, 0);
196 gpiod_set_value(stk->enable_gpio, 0);
197
198 return ret;
199}
200
201static const struct drm_display_mode default_mode = {
202 .clock = 163204,
203 .hdisplay = 1200,
204 .hsync_start = 1200 + 144,
205 .hsync_end = 1200 + 144 + 16,
206 .htotal = 1200 + 144 + 16 + 45,
207 .vdisplay = 1920,
208 .vsync_start = 1920 + 8,
209 .vsync_end = 1920 + 8 + 4,
210 .vtotal = 1920 + 8 + 4 + 4,
211 .width_mm = 95,
212 .height_mm = 151,
213};
214
215static int stk_panel_get_modes(struct drm_panel *panel,
216 struct drm_connector *connector)
217{
218 struct drm_display_mode *mode;
219
220 mode = drm_mode_duplicate(connector->dev, &default_mode);
221 if (!mode) {
222 dev_err(panel->dev, "failed to add mode %ux%ux@%u\n",
223 default_mode.hdisplay, default_mode.vdisplay,
224 drm_mode_vrefresh(&default_mode));
225 return -ENOMEM;
226 }
227
228 drm_mode_set_name(mode);
229 drm_mode_probed_add(connector, mode);
230 connector->display_info.width_mm = default_mode.width_mm;
231 connector->display_info.height_mm = default_mode.height_mm;
232 return 1;
233}
234
235static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)
236{
237 struct mipi_dsi_device *dsi = bl_get_data(bl);
238 int ret;
239 u16 brightness;
240
241 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
242 ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
243 if (ret < 0)
244 return ret;
245
246 dsi->mode_flags |= MIPI_DSI_MODE_LPM;
247 return brightness & 0xff;
248}
249
250static int dsi_dcs_bl_update_status(struct backlight_device *bl)
251{
252 struct mipi_dsi_device *dsi = bl_get_data(bl);
253 struct device *dev = &dsi->dev;
254 int ret;
255
256 dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
257 ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
258 if (ret < 0) {
259 dev_err(dev, "failed to set DSI control: %d\n", ret);
260 return ret;
261 }
262
263 dsi->mode_flags |= MIPI_DSI_MODE_LPM;
264 return 0;
265}
266
267static const struct backlight_ops dsi_bl_ops = {
268 .update_status = dsi_dcs_bl_update_status,
269 .get_brightness = dsi_dcs_bl_get_brightness,
270};
271
272static struct backlight_device *
273drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
274{
275 struct device *dev = &dsi->dev;
276 struct backlight_properties props = {
277 .type = BACKLIGHT_RAW,
278 .brightness = 255,
279 .max_brightness = 255,
280 };
281
282 return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
283 &dsi_bl_ops, &props);
284}
285
286static const struct drm_panel_funcs stk_panel_funcs = {
287 .unprepare = stk_panel_unprepare,
288 .prepare = stk_panel_prepare,
289 .get_modes = stk_panel_get_modes,
290};
291
292static const struct of_device_id stk_of_match[] = {
293 { .compatible = "startek,kd070fhfid015", },
294 { }
295};
296MODULE_DEVICE_TABLE(of, stk_of_match);
297
298static int stk_panel_add(struct stk_panel *stk)
299{
300 struct device *dev = &stk->dsi->dev;
301 int ret;
302
303 stk->mode = &default_mode;
304
305 stk->supplies[IOVCC].supply = "iovcc";
306 stk->supplies[POWER].supply = "power";
307 ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies);
308 if (ret) {
309 dev_err(dev, "regulator_bulk failed\n");
310 return ret;
311 }
312
313 stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
314 if (IS_ERR(stk->reset_gpio)) {
315 ret = PTR_ERR(stk->reset_gpio);
316 dev_err(dev, "cannot get reset-gpios %d\n", ret);
317 return ret;
318 }
319
320 stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
321 if (IS_ERR(stk->enable_gpio)) {
322 ret = PTR_ERR(stk->enable_gpio);
323 dev_err(dev, "cannot get enable-gpio %d\n", ret);
324 return ret;
325 }
326
327 stk->backlight = drm_panel_create_dsi_backlight(stk->dsi);
328 if (IS_ERR(stk->backlight)) {
329 ret = PTR_ERR(stk->backlight);
330 dev_err(dev, "failed to register backlight %d\n", ret);
331 return ret;
332 }
333
334 drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs,
335 DRM_MODE_CONNECTOR_DSI);
336
337 drm_panel_add(&stk->base);
338
339 return 0;
340}
341
342static int stk_panel_probe(struct mipi_dsi_device *dsi)
343{
344 struct stk_panel *stk;
345 int ret;
346
347 dsi->lanes = 4;
348 dsi->format = MIPI_DSI_FMT_RGB888;
349 dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM);
350
351 stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL);
352 if (!stk)
353 return -ENOMEM;
354
355 mipi_dsi_set_drvdata(dsi, stk);
356
357 stk->dsi = dsi;
358
359 ret = stk_panel_add(stk);
360 if (ret < 0)
361 return ret;
362
363 ret = mipi_dsi_attach(dsi);
364 if (ret < 0)
365 drm_panel_remove(&stk->base);
366
367 return 0;
368}
369
370static void stk_panel_remove(struct mipi_dsi_device *dsi)
371{
372 struct stk_panel *stk = mipi_dsi_get_drvdata(dsi);
373 int err;
374
375 err = mipi_dsi_detach(dsi);
376 if (err < 0)
377 dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
378 err);
379
380 drm_panel_remove(&stk->base);
381}
382
383static struct mipi_dsi_driver stk_panel_driver = {
384 .driver = {
385 .name = "panel-startek-kd070fhfid015",
386 .of_match_table = stk_of_match,
387 },
388 .probe = stk_panel_probe,
389 .remove = stk_panel_remove,
390};
391module_mipi_dsi_driver(stk_panel_driver);
392
393MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>");
394MODULE_DESCRIPTION("STARTEK KD070FHFID015");
395MODULE_LICENSE("GPL");