]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c
drm/panel: Add and fill drm_panel type field
[linux.git] / drivers / gpu / drm / panel / panel-feiyang-fy07024di26a30d.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2018 Amarula Solutions
4  * Author: Jagan Teki <jagan@amarulasolutions.com>
5  */
6
7 #include <drm/drm_mipi_dsi.h>
8 #include <drm/drm_modes.h>
9 #include <drm/drm_panel.h>
10 #include <drm/drm_print.h>
11
12 #include <linux/backlight.h>
13 #include <linux/gpio/consumer.h>
14 #include <linux/delay.h>
15 #include <linux/module.h>
16 #include <linux/of_device.h>
17 #include <linux/regulator/consumer.h>
18
19 #define FEIYANG_INIT_CMD_LEN    2
20
21 struct feiyang {
22         struct drm_panel        panel;
23         struct mipi_dsi_device  *dsi;
24
25         struct backlight_device *backlight;
26         struct regulator        *dvdd;
27         struct regulator        *avdd;
28         struct gpio_desc        *reset;
29 };
30
31 static inline struct feiyang *panel_to_feiyang(struct drm_panel *panel)
32 {
33         return container_of(panel, struct feiyang, panel);
34 }
35
36 struct feiyang_init_cmd {
37         u8 data[FEIYANG_INIT_CMD_LEN];
38 };
39
40 static const struct feiyang_init_cmd feiyang_init_cmds[] = {
41         { .data = { 0x80, 0x58 } },
42         { .data = { 0x81, 0x47 } },
43         { .data = { 0x82, 0xD4 } },
44         { .data = { 0x83, 0x88 } },
45         { .data = { 0x84, 0xA9 } },
46         { .data = { 0x85, 0xC3 } },
47         { .data = { 0x86, 0x82 } },
48 };
49
50 static int feiyang_prepare(struct drm_panel *panel)
51 {
52         struct feiyang *ctx = panel_to_feiyang(panel);
53         struct mipi_dsi_device *dsi = ctx->dsi;
54         unsigned int i;
55         int ret;
56
57         ret = regulator_enable(ctx->dvdd);
58         if (ret)
59                 return ret;
60
61         /* T1 (dvdd start + dvdd rise) 0 < T1 <= 10ms */
62         msleep(10);
63
64         ret = regulator_enable(ctx->avdd);
65         if (ret)
66                 return ret;
67
68         /* T3 (dvdd rise + avdd start + avdd rise) T3 >= 20ms */
69         msleep(20);
70
71         gpiod_set_value(ctx->reset, 0);
72
73         /*
74          * T5 + T6 (avdd rise + video & logic signal rise)
75          * T5 >= 10ms, 0 < T6 <= 10ms
76          */
77         msleep(20);
78
79         gpiod_set_value(ctx->reset, 1);
80
81         /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */
82         msleep(200);
83
84         for (i = 0; i < ARRAY_SIZE(feiyang_init_cmds); i++) {
85                 const struct feiyang_init_cmd *cmd =
86                                                 &feiyang_init_cmds[i];
87
88                 ret = mipi_dsi_dcs_write_buffer(dsi, cmd->data,
89                                                 FEIYANG_INIT_CMD_LEN);
90                 if (ret < 0)
91                         return ret;
92         }
93
94         return 0;
95 }
96
97 static int feiyang_enable(struct drm_panel *panel)
98 {
99         struct feiyang *ctx = panel_to_feiyang(panel);
100
101         /* T12 (video & logic signal rise + backlight rise) T12 >= 200ms */
102         msleep(200);
103
104         mipi_dsi_dcs_set_display_on(ctx->dsi);
105         backlight_enable(ctx->backlight);
106
107         return 0;
108 }
109
110 static int feiyang_disable(struct drm_panel *panel)
111 {
112         struct feiyang *ctx = panel_to_feiyang(panel);
113
114         backlight_disable(ctx->backlight);
115         return mipi_dsi_dcs_set_display_off(ctx->dsi);
116 }
117
118 static int feiyang_unprepare(struct drm_panel *panel)
119 {
120         struct feiyang *ctx = panel_to_feiyang(panel);
121         int ret;
122
123         ret = mipi_dsi_dcs_set_display_off(ctx->dsi);
124         if (ret < 0)
125                 DRM_DEV_ERROR(panel->dev, "failed to set display off: %d\n",
126                               ret);
127
128         ret = mipi_dsi_dcs_enter_sleep_mode(ctx->dsi);
129         if (ret < 0)
130                 DRM_DEV_ERROR(panel->dev, "failed to enter sleep mode: %d\n",
131                               ret);
132
133         /* T13 (backlight fall + video & logic signal fall) T13 >= 200ms */
134         msleep(200);
135
136         gpiod_set_value(ctx->reset, 0);
137
138         regulator_disable(ctx->avdd);
139
140         /* T11 (dvdd rise to fall) 0 < T11 <= 10ms  */
141         msleep(10);
142
143         regulator_disable(ctx->dvdd);
144
145         return 0;
146 }
147
148 static const struct drm_display_mode feiyang_default_mode = {
149         .clock          = 55000,
150
151         .hdisplay       = 1024,
152         .hsync_start    = 1024 + 310,
153         .hsync_end      = 1024 + 310 + 20,
154         .htotal         = 1024 + 310 + 20 + 90,
155
156         .vdisplay       = 600,
157         .vsync_start    = 600 + 12,
158         .vsync_end      = 600 + 12 + 2,
159         .vtotal         = 600 + 12 + 2 + 21,
160         .vrefresh       = 60,
161
162         .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
163 };
164
165 static int feiyang_get_modes(struct drm_panel *panel)
166 {
167         struct drm_connector *connector = panel->connector;
168         struct feiyang *ctx = panel_to_feiyang(panel);
169         struct drm_display_mode *mode;
170
171         mode = drm_mode_duplicate(panel->drm, &feiyang_default_mode);
172         if (!mode) {
173                 DRM_DEV_ERROR(&ctx->dsi->dev, "failed to add mode %ux%ux@%u\n",
174                               feiyang_default_mode.hdisplay,
175                               feiyang_default_mode.vdisplay,
176                               feiyang_default_mode.vrefresh);
177                 return -ENOMEM;
178         }
179
180         drm_mode_set_name(mode);
181
182         drm_mode_probed_add(connector, mode);
183
184         return 1;
185 }
186
187 static const struct drm_panel_funcs feiyang_funcs = {
188         .disable = feiyang_disable,
189         .unprepare = feiyang_unprepare,
190         .prepare = feiyang_prepare,
191         .enable = feiyang_enable,
192         .get_modes = feiyang_get_modes,
193 };
194
195 static int feiyang_dsi_probe(struct mipi_dsi_device *dsi)
196 {
197         struct feiyang *ctx;
198         int ret;
199
200         ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
201         if (!ctx)
202                 return -ENOMEM;
203
204         mipi_dsi_set_drvdata(dsi, ctx);
205         ctx->dsi = dsi;
206
207         drm_panel_init(&ctx->panel, &dsi->dev, &feiyang_funcs,
208                        DRM_MODE_CONNECTOR_DSI);
209
210         ctx->dvdd = devm_regulator_get(&dsi->dev, "dvdd");
211         if (IS_ERR(ctx->dvdd)) {
212                 DRM_DEV_ERROR(&dsi->dev, "Couldn't get dvdd regulator\n");
213                 return PTR_ERR(ctx->dvdd);
214         }
215
216         ctx->avdd = devm_regulator_get(&dsi->dev, "avdd");
217         if (IS_ERR(ctx->avdd)) {
218                 DRM_DEV_ERROR(&dsi->dev, "Couldn't get avdd regulator\n");
219                 return PTR_ERR(ctx->avdd);
220         }
221
222         ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_LOW);
223         if (IS_ERR(ctx->reset)) {
224                 DRM_DEV_ERROR(&dsi->dev, "Couldn't get our reset GPIO\n");
225                 return PTR_ERR(ctx->reset);
226         }
227
228         ctx->backlight = devm_of_find_backlight(&dsi->dev);
229         if (IS_ERR(ctx->backlight))
230                 return PTR_ERR(ctx->backlight);
231
232         ret = drm_panel_add(&ctx->panel);
233         if (ret < 0)
234                 return ret;
235
236         dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST;
237         dsi->format = MIPI_DSI_FMT_RGB888;
238         dsi->lanes = 4;
239
240         return mipi_dsi_attach(dsi);
241 }
242
243 static int feiyang_dsi_remove(struct mipi_dsi_device *dsi)
244 {
245         struct feiyang *ctx = mipi_dsi_get_drvdata(dsi);
246
247         mipi_dsi_detach(dsi);
248         drm_panel_remove(&ctx->panel);
249
250         return 0;
251 }
252
253 static const struct of_device_id feiyang_of_match[] = {
254         { .compatible = "feiyang,fy07024di26a30d", },
255         { /* sentinel */ }
256 };
257 MODULE_DEVICE_TABLE(of, feiyang_of_match);
258
259 static struct mipi_dsi_driver feiyang_driver = {
260         .probe = feiyang_dsi_probe,
261         .remove = feiyang_dsi_remove,
262         .driver = {
263                 .name = "feiyang-fy07024di26a30d",
264                 .of_match_table = feiyang_of_match,
265         },
266 };
267 module_mipi_dsi_driver(feiyang_driver);
268
269 MODULE_AUTHOR("Jagan Teki <jagan@amarulasolutions.com>");
270 MODULE_DESCRIPTION("Feiyang FY07024DI26A30-D MIPI-DSI LCD panel");
271 MODULE_LICENSE("GPL");