Skip to content

Commit 2ae3edd

Browse files
committed
drm/panel: Add Sharp Y030XX067A 3.0" 320x480 panel
Signed-off-by: Paul Cercueil <[email protected]>
1 parent f171211 commit 2ae3edd

File tree

3 files changed

+261
-0
lines changed

3 files changed

+261
-0
lines changed

drivers/gpu/drm/panel/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,14 @@ config DRM_PANEL_SHARP_LS043T1LE01
390390
Say Y here if you want to enable support for Sharp LS043T1LE01 qHD
391391
(540x960) DSI panel as found on the Qualcomm APQ8074 Dragonboard
392392

393+
config DRM_PANEL_SHARP_Y030XX067A
394+
tristate "Sharp Y030XX067A 320x480 LCD panel"
395+
depends on OF && SPI
396+
select REGMAP_SPI
397+
help
398+
Say Y here to enable support for the Sharp Y030XX067A 320x480 3.0"
399+
panel as found on the YLM RG-99 handheld gaming console.
400+
393401
config DRM_PANEL_SITRONIX_ST7701
394402
tristate "Sitronix ST7701 panel driver"
395403
depends on OF

drivers/gpu/drm/panel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
4141
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
4242
obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
4343
obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
44+
obj-$(CONFIG_DRM_PANEL_SHARP_Y030XX067A) += panel-sharp-y030xx067a.o
4445
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
4546
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
4647
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Sharp Y030XX067A IPS LCD panel driver
4+
*
5+
* Copyright (C) 2020, Paul Cercueil <[email protected]>
6+
*/
7+
8+
#include <linux/delay.h>
9+
#include <linux/device.h>
10+
#include <linux/gpio/consumer.h>
11+
#include <linux/media-bus-format.h>
12+
#include <linux/module.h>
13+
#include <linux/of_device.h>
14+
#include <linux/regmap.h>
15+
#include <linux/regulator/consumer.h>
16+
#include <linux/spi/spi.h>
17+
18+
#include <drm/drm_modes.h>
19+
#include <drm/drm_panel.h>
20+
21+
#include <video/mipi_display.h>
22+
23+
struct y030xx067a_info {
24+
const struct drm_display_mode *display_modes;
25+
unsigned int num_modes;
26+
u16 width_mm, height_mm;
27+
u32 bus_format, bus_flags;
28+
};
29+
30+
struct y030xx067a {
31+
struct drm_panel panel;
32+
struct spi_device *spi;
33+
struct regmap *map;
34+
35+
const struct y030xx067a_info *panel_info;
36+
37+
struct regulator *supply;
38+
struct gpio_desc *reset_gpio;
39+
};
40+
41+
static inline struct y030xx067a *to_y030xx067a(struct drm_panel *panel)
42+
{
43+
return container_of(panel, struct y030xx067a, panel);
44+
}
45+
46+
static const struct reg_sequence y030xx067a_init_sequence[] = {
47+
{ 0x02, 0x7f, },
48+
{ 0x03, 0x0a, },
49+
{ 0x04, 0x80, },
50+
{ 0x06, 0x90, },
51+
{ 0x08, 0x28, },
52+
{ 0x09, 0x20, },
53+
{ 0x0a, 0x20, },
54+
{ 0x0c, 0x10, },
55+
{ 0x0d, 0x10, },
56+
{ 0x0e, 0x90, },
57+
{ 0x10, 0x7f, },
58+
{ 0x11, 0x3f, },
59+
};
60+
61+
static int y030xx067a_prepare(struct drm_panel *panel)
62+
{
63+
struct y030xx067a *priv = to_y030xx067a(panel);
64+
struct device *dev = &priv->spi->dev;
65+
int err;
66+
67+
err = regulator_enable(priv->supply);
68+
if (err) {
69+
dev_err(dev, "Failed to enable power supply: %d\n", err);
70+
return err;
71+
}
72+
73+
/* Reset the chip */
74+
gpiod_set_value_cansleep(priv->reset_gpio, 1);
75+
usleep_range(1000, 20000);
76+
gpiod_set_value_cansleep(priv->reset_gpio, 0);
77+
usleep_range(1000, 20000);
78+
79+
err = regmap_multi_reg_write(priv->map, y030xx067a_init_sequence,
80+
ARRAY_SIZE(y030xx067a_init_sequence));
81+
if (err) {
82+
dev_err(dev, "Failed to init registers: %d\n", err);
83+
goto err_disable_regulator;
84+
}
85+
86+
return 0;
87+
88+
err_disable_regulator:
89+
regulator_disable(priv->supply);
90+
return err;
91+
}
92+
93+
static int y030xx067a_unprepare(struct drm_panel *panel)
94+
{
95+
struct y030xx067a *priv = to_y030xx067a(panel);
96+
97+
gpiod_set_value_cansleep(priv->reset_gpio, 1);
98+
regulator_disable(priv->supply);
99+
100+
return 0;
101+
}
102+
103+
static int y030xx067a_get_modes(struct drm_panel *panel,
104+
struct drm_connector *connector)
105+
{
106+
struct y030xx067a *priv = to_y030xx067a(panel);
107+
const struct y030xx067a_info *panel_info = priv->panel_info;
108+
struct drm_display_mode *mode;
109+
unsigned int i;
110+
111+
for (i = 0; i < panel_info->num_modes; i++) {
112+
mode = drm_mode_duplicate(connector->dev,
113+
&panel_info->display_modes[i]);
114+
if (!mode)
115+
return -ENOMEM;
116+
117+
drm_mode_set_name(mode);
118+
119+
mode->type = DRM_MODE_TYPE_DRIVER;
120+
if (panel_info->num_modes == 1)
121+
mode->type |= DRM_MODE_TYPE_PREFERRED;
122+
123+
drm_mode_probed_add(connector, mode);
124+
}
125+
126+
connector->display_info.bpc = 6;
127+
connector->display_info.width_mm = panel_info->width_mm;
128+
connector->display_info.height_mm = panel_info->height_mm;
129+
130+
drm_display_info_set_bus_formats(&connector->display_info,
131+
&panel_info->bus_format, 1);
132+
connector->display_info.bus_flags = panel_info->bus_flags;
133+
134+
return panel_info->num_modes;
135+
}
136+
137+
static const struct drm_panel_funcs y030xx067a_funcs = {
138+
.prepare = y030xx067a_prepare,
139+
.unprepare = y030xx067a_unprepare,
140+
.get_modes = y030xx067a_get_modes,
141+
};
142+
143+
static const struct regmap_config y030xx067a_regmap_config = {
144+
.reg_bits = 8,
145+
.val_bits = 8,
146+
.max_register = 0x15,
147+
};
148+
149+
static int y030xx067a_probe(struct spi_device *spi)
150+
{
151+
struct device *dev = &spi->dev;
152+
struct y030xx067a *priv;
153+
int err;
154+
155+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
156+
if (!priv)
157+
return -ENOMEM;
158+
159+
priv->spi = spi;
160+
spi_set_drvdata(spi, priv);
161+
162+
priv->map = devm_regmap_init_spi(spi, &y030xx067a_regmap_config);
163+
if (IS_ERR(priv->map)) {
164+
dev_err(dev, "Unable to init regmap\n");
165+
return PTR_ERR(priv->map);
166+
}
167+
168+
priv->panel_info = of_device_get_match_data(dev);
169+
if (!priv->panel_info)
170+
return -EINVAL;
171+
172+
priv->supply = devm_regulator_get(dev, "power");
173+
if (IS_ERR(priv->supply)) {
174+
dev_err(dev, "Failed to get power supply\n");
175+
return PTR_ERR(priv->supply);
176+
}
177+
178+
priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
179+
if (IS_ERR(priv->reset_gpio)) {
180+
dev_err(dev, "Failed to get reset GPIO\n");
181+
return PTR_ERR(priv->reset_gpio);
182+
}
183+
184+
drm_panel_init(&priv->panel, dev, &y030xx067a_funcs,
185+
DRM_MODE_CONNECTOR_DPI);
186+
187+
err = drm_panel_of_backlight(&priv->panel);
188+
if (err)
189+
return err;
190+
191+
err = drm_panel_add(&priv->panel);
192+
if (err < 0) {
193+
dev_err(dev, "Failed to add panel\n");
194+
return err;
195+
}
196+
197+
return 0;
198+
}
199+
200+
static int y030xx067a_remove(struct spi_device *spi)
201+
{
202+
struct y030xx067a *priv = spi_get_drvdata(spi);
203+
204+
drm_panel_remove(&priv->panel);
205+
drm_panel_disable(&priv->panel);
206+
drm_panel_unprepare(&priv->panel);
207+
208+
return 0;
209+
}
210+
211+
static const struct drm_display_mode y030xx067a_modes[] = {
212+
{
213+
.clock = 54000,
214+
.hdisplay = 320,
215+
.hsync_start = 320 + 128,
216+
.hsync_end = 320 + 128 + 28,
217+
.htotal = 320 + 128 + 28 + 25,
218+
.vdisplay = 480,
219+
.vsync_start = 480 + 36,
220+
.vsync_end = 480 + 36 + 1,
221+
.vtotal = 480 + 36 + 1 + 16,
222+
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
223+
},
224+
};
225+
226+
static const struct y030xx067a_info y030xx067a_info = {
227+
.display_modes = y030xx067a_modes,
228+
.num_modes = ARRAY_SIZE(y030xx067a_modes),
229+
.width_mm = 69,
230+
.height_mm = 51,
231+
.bus_format = MEDIA_BUS_FMT_RGB565_2X8_LE,
232+
.bus_flags = 0,
233+
};
234+
235+
static const struct of_device_id y030xx067a_of_match[] = {
236+
{ .compatible = "sharp,y030xx067a", .data = &y030xx067a_info },
237+
{ /* sentinel */ }
238+
};
239+
MODULE_DEVICE_TABLE(of, y030xx067a_of_match);
240+
241+
static struct spi_driver y030xx067a_driver = {
242+
.driver = {
243+
.name = "sharp-y030xx067a",
244+
.of_match_table = y030xx067a_of_match,
245+
},
246+
.probe = y030xx067a_probe,
247+
.remove = y030xx067a_remove,
248+
};
249+
module_spi_driver(y030xx067a_driver);
250+
251+
MODULE_AUTHOR("Paul Cercueil <[email protected]>");
252+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)