Lines Matching +full:panel +full:- +full:mipi +full:- +full:dbi +full:- +full:spi
1 // SPDX-License-Identifier: GPL-2.0
3 * Panel driver for the WideChips WS2401 480x800 DPI RGB panel, used in
5 * Found in the Samsung Galaxy Ace 2 GT-I8160 mobile phone.
7 * Inspired by code and know-how in the vendor driver by Gareth Phillips.
18 #include <linux/media-bus-format.h>
21 #include <linux/spi/spi.h>
34 #define WS2401_READ_ID1 0xda /* Read panel ID 1 */
35 #define WS2401_READ_ID2 0xdb /* Read panel ID 2 */
36 #define WS2401_READ_ID3 0xdc /* Read panel ID 3 */
48 #define WS2401_PANELCTL 0xf6 /* Panel control */
58 * struct ws2401 - state container for a panel controlled by the WS2401
64 /** @dbi: the DBI bus abstraction handle */
65 struct mipi_dbi dbi; member
66 /** @panel: the DRM panel instance for this device */
67 struct drm_panel panel; member
68 /** @width: the width of this panel in mm */
70 /** @height: the height of this panel in mm */
82 * The vendor driver states that the "SMD panel" has a clock
99 static inline struct ws2401 *to_ws2401(struct drm_panel *panel) in to_ws2401() argument
101 return container_of(panel, struct ws2401, panel); in to_ws2401()
106 struct mipi_dbi *dbi = &ws->dbi; in ws2401_read_mtp_id() local
110 ret = mipi_dbi_command_read(dbi, WS2401_READ_ID1, &id1); in ws2401_read_mtp_id()
112 dev_err(ws->dev, "unable to read MTP ID 1\n"); in ws2401_read_mtp_id()
115 ret = mipi_dbi_command_read(dbi, WS2401_READ_ID2, &id2); in ws2401_read_mtp_id()
117 dev_err(ws->dev, "unable to read MTP ID 2\n"); in ws2401_read_mtp_id()
120 ret = mipi_dbi_command_read(dbi, WS2401_READ_ID3, &id3); in ws2401_read_mtp_id()
122 dev_err(ws->dev, "unable to read MTP ID 3\n"); in ws2401_read_mtp_id()
125 dev_info(ws->dev, "MTP ID: %02x %02x %02x\n", id1, id2, id3); in ws2401_read_mtp_id()
130 struct mipi_dbi *dbi = &ws->dbi; in ws2401_power_on() local
134 ret = regulator_bulk_enable(ARRAY_SIZE(ws->regulators), in ws2401_power_on()
135 ws->regulators); in ws2401_power_on()
137 dev_err(ws->dev, "failed to enable regulators: %d\n", ret); in ws2401_power_on()
143 gpiod_set_value_cansleep(ws->reset, 1); in ws2401_power_on()
145 /* De-assert reset */ in ws2401_power_on()
146 gpiod_set_value_cansleep(ws->reset, 0); in ws2401_power_on()
149 dev_dbg(ws->dev, "de-asserted RESET\n"); in ws2401_power_on()
152 * Exit sleep mode and initialize display - some hammering is in ws2401_power_on()
155 mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); in ws2401_power_on()
156 mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); in ws2401_power_on()
160 mipi_dbi_command(dbi, WS2401_PASSWD1, 0x5a, 0x5a); in ws2401_power_on()
162 mipi_dbi_command(dbi, WS2401_RESCTL, 0x12); in ws2401_power_on()
164 mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, 0x01); in ws2401_power_on()
166 mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, 0x70); in ws2401_power_on()
167 mipi_dbi_command(dbi, WS2401_SMPS, 0x00, 0x0f); in ws2401_power_on()
168 mipi_dbi_command(dbi, WS2401_PSMPS, 0x06, 0x03, /* DDVDH: 4.6v */ in ws2401_power_on()
170 mipi_dbi_command(dbi, WS2401_NSMPS, 0x06, 0x03, /* DDVDH: -4.6v */ in ws2401_power_on()
172 mipi_dbi_command(dbi, WS2401_SMPS, 0x02, 0x0f); in ws2401_power_on()
173 mipi_dbi_command(dbi, WS2401_PWRCTL, 0x10, 0xA9, 0x00, 0x01, 0x44, in ws2401_power_on()
174 0xb4, /* VGH:16.1v, VGL:-13.8v */ in ws2401_power_on()
176 0x50, /* GREFN:-4.2v (default) */ in ws2401_power_on()
178 0x44); /* VOUTL:-10v (default) */ in ws2401_power_on()
179 mipi_dbi_command(dbi, WS2401_DISCTL, 0x01, 0x00, 0x00, 0x00, 0x14, in ws2401_power_on()
181 mipi_dbi_command(dbi, WS2401_VCOMCTL, 0x30, 0x53, 0x53); in ws2401_power_on()
182 mipi_dbi_command(dbi, WS2401_SRCCTL, 0x03, 0x0C, 0x00, 0x00, 0x00, in ws2401_power_on()
185 mipi_dbi_command(dbi, WS2401_PANELCTL, 0x14, 0x00, 0x80, 0x00); in ws2401_power_on()
186 mipi_dbi_command(dbi, WS2401_WRMIE, 0x01); in ws2401_power_on()
188 /* Set up gamma, probably these are P-gamma and N-gamma for each color */ in ws2401_power_on()
189 mipi_dbi_command(dbi, WS2401_GAMMA_R1, 0x00, in ws2401_power_on()
192 mipi_dbi_command(dbi, WS2401_GAMMA_R2, 0x00, in ws2401_power_on()
195 mipi_dbi_command(dbi, WS2401_GAMMA_G1, 0x00, in ws2401_power_on()
198 mipi_dbi_command(dbi, WS2401_GAMMA_G2, 0x00, in ws2401_power_on()
201 mipi_dbi_command(dbi, WS2401_GAMMA_B1, 0x00, in ws2401_power_on()
204 mipi_dbi_command(dbi, WS2401_GAMMA_B2, 0x00, in ws2401_power_on()
208 if (ws->internal_bl) { in ws2401_power_on()
209 mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x2c); in ws2401_power_on()
211 mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x00); in ws2401_power_on()
214 * L2 accesses to the panel so we close the door on our way out. in ws2401_power_on()
217 mipi_dbi_command(dbi, WS2401_PASSWD1, 0xa5, 0xa5); in ws2401_power_on()
226 gpiod_set_value_cansleep(ws->reset, 1); in ws2401_power_off()
227 return regulator_bulk_disable(ARRAY_SIZE(ws->regulators), in ws2401_power_off()
228 ws->regulators); in ws2401_power_off()
231 static int ws2401_unprepare(struct drm_panel *panel) in ws2401_unprepare() argument
233 struct ws2401 *ws = to_ws2401(panel); in ws2401_unprepare()
234 struct mipi_dbi *dbi = &ws->dbi; in ws2401_unprepare() local
237 if (ws->internal_bl) in ws2401_unprepare()
238 mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x00); in ws2401_unprepare()
239 mipi_dbi_command(dbi, MIPI_DCS_ENTER_SLEEP_MODE); in ws2401_unprepare()
241 return ws2401_power_off(to_ws2401(panel)); in ws2401_unprepare()
244 static int ws2401_disable(struct drm_panel *panel) in ws2401_disable() argument
246 struct ws2401 *ws = to_ws2401(panel); in ws2401_disable()
247 struct mipi_dbi *dbi = &ws->dbi; in ws2401_disable() local
249 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); in ws2401_disable()
255 static int ws2401_prepare(struct drm_panel *panel) in ws2401_prepare() argument
257 return ws2401_power_on(to_ws2401(panel)); in ws2401_prepare()
260 static int ws2401_enable(struct drm_panel *panel) in ws2401_enable() argument
262 struct ws2401 *ws = to_ws2401(panel); in ws2401_enable()
263 struct mipi_dbi *dbi = &ws->dbi; in ws2401_enable() local
265 mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); in ws2401_enable()
271 * ws2401_get_modes() - return the mode
272 * @panel: the panel to get the mode for
275 static int ws2401_get_modes(struct drm_panel *panel, in ws2401_get_modes() argument
278 struct ws2401 *ws = to_ws2401(panel); in ws2401_get_modes()
286 * the target panel needs. in ws2401_get_modes()
288 mode = drm_mode_duplicate(connector->dev, &lms380kf01_480_800_mode); in ws2401_get_modes()
290 dev_err(ws->dev, "failed to add mode\n"); in ws2401_get_modes()
291 return -ENOMEM; in ws2401_get_modes()
294 connector->display_info.bpc = 8; in ws2401_get_modes()
295 connector->display_info.width_mm = mode->width_mm; in ws2401_get_modes()
296 connector->display_info.height_mm = mode->height_mm; in ws2401_get_modes()
297 connector->display_info.bus_flags = in ws2401_get_modes()
299 drm_display_info_set_bus_formats(&connector->display_info, in ws2401_get_modes()
303 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; in ws2401_get_modes()
321 struct mipi_dbi *dbi = &ws->dbi; in ws2401_set_brightness() local
325 mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x00); in ws2401_set_brightness()
327 mipi_dbi_command(dbi, WS2401_WRCTRLD, 0x2c); in ws2401_set_brightness()
328 mipi_dbi_command(dbi, WS2401_WRDISBV, brightness); in ws2401_set_brightness()
344 static int ws2401_probe(struct spi_device *spi) in ws2401_probe() argument
346 struct device *dev = &spi->dev; in ws2401_probe()
352 return -ENOMEM; in ws2401_probe()
353 ws->dev = dev; in ws2401_probe()
359 ws->regulators[0].supply = "vci"; in ws2401_probe()
360 ws->regulators[1].supply = "vccio"; in ws2401_probe()
362 ARRAY_SIZE(ws->regulators), in ws2401_probe()
363 ws->regulators); in ws2401_probe()
367 ws->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); in ws2401_probe()
368 if (IS_ERR(ws->reset)) { in ws2401_probe()
369 ret = PTR_ERR(ws->reset); in ws2401_probe()
373 ret = mipi_dbi_spi_init(spi, &ws->dbi, NULL); in ws2401_probe()
375 return dev_err_probe(dev, ret, "MIPI DBI init failed\n"); in ws2401_probe()
376 ws->dbi.read_commands = ws2401_dbi_read_commands; in ws2401_probe()
382 drm_panel_init(&ws->panel, dev, &ws2401_drm_funcs, in ws2401_probe()
385 ret = drm_panel_of_backlight(&ws->panel); in ws2401_probe()
390 if (!ws->panel.backlight) { in ws2401_probe()
392 ws->panel.backlight = in ws2401_probe()
395 if (IS_ERR(ws->panel.backlight)) in ws2401_probe()
396 return dev_err_probe(dev, PTR_ERR(ws->panel.backlight), in ws2401_probe()
402 spi_set_drvdata(spi, ws); in ws2401_probe()
404 drm_panel_add(&ws->panel); in ws2401_probe()
405 dev_dbg(dev, "added panel\n"); in ws2401_probe()
410 static void ws2401_remove(struct spi_device *spi) in ws2401_remove() argument
412 struct ws2401 *ws = spi_get_drvdata(spi); in ws2401_remove()
414 drm_panel_remove(&ws->panel); in ws2401_remove()
432 MODULE_DEVICE_TABLE(spi, ws2401_ids);
439 .name = "ws2401-panel",
446 MODULE_DESCRIPTION("Samsung WS2401 panel driver");