xref: /openbmc/linux/drivers/gpu/drm/tiny/ili9163.c (revision 50848e37)
1*50848e37SDaniel Mack // SPDX-License-Identifier: GPL-2.0+
2*50848e37SDaniel Mack 
3*50848e37SDaniel Mack #include <linux/backlight.h>
4*50848e37SDaniel Mack #include <linux/delay.h>
5*50848e37SDaniel Mack #include <linux/gpio/consumer.h>
6*50848e37SDaniel Mack #include <linux/module.h>
7*50848e37SDaniel Mack #include <linux/property.h>
8*50848e37SDaniel Mack #include <linux/spi/spi.h>
9*50848e37SDaniel Mack 
10*50848e37SDaniel Mack #include <drm/drm_atomic_helper.h>
11*50848e37SDaniel Mack #include <drm/drm_drv.h>
12*50848e37SDaniel Mack #include <drm/drm_fb_helper.h>
13*50848e37SDaniel Mack #include <drm/drm_gem_cma_helper.h>
14*50848e37SDaniel Mack #include <drm/drm_gem_framebuffer_helper.h>
15*50848e37SDaniel Mack #include <drm/drm_mipi_dbi.h>
16*50848e37SDaniel Mack #include <drm/drm_modeset_helper.h>
17*50848e37SDaniel Mack #include <video/mipi_display.h>
18*50848e37SDaniel Mack 
19*50848e37SDaniel Mack #define ILI9163_FRMCTR1		0xb1
20*50848e37SDaniel Mack 
21*50848e37SDaniel Mack #define ILI9163_PWCTRL1		0xc0
22*50848e37SDaniel Mack #define ILI9163_PWCTRL2		0xc1
23*50848e37SDaniel Mack #define ILI9163_VMCTRL1		0xc5
24*50848e37SDaniel Mack #define ILI9163_VMCTRL2		0xc7
25*50848e37SDaniel Mack #define ILI9163_PWCTRLA		0xcb
26*50848e37SDaniel Mack #define ILI9163_PWCTRLB		0xcf
27*50848e37SDaniel Mack 
28*50848e37SDaniel Mack #define ILI9163_EN3GAM		0xf2
29*50848e37SDaniel Mack 
30*50848e37SDaniel Mack #define ILI9163_MADCTL_BGR	BIT(3)
31*50848e37SDaniel Mack #define ILI9163_MADCTL_MV	BIT(5)
32*50848e37SDaniel Mack #define ILI9163_MADCTL_MX	BIT(6)
33*50848e37SDaniel Mack #define ILI9163_MADCTL_MY	BIT(7)
34*50848e37SDaniel Mack 
35*50848e37SDaniel Mack static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
36*50848e37SDaniel Mack 			     struct drm_crtc_state *crtc_state,
37*50848e37SDaniel Mack 			     struct drm_plane_state *plane_state)
38*50848e37SDaniel Mack {
39*50848e37SDaniel Mack 	struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
40*50848e37SDaniel Mack 	struct mipi_dbi *dbi = &dbidev->dbi;
41*50848e37SDaniel Mack 	u8 addr_mode;
42*50848e37SDaniel Mack 	int ret, idx;
43*50848e37SDaniel Mack 
44*50848e37SDaniel Mack 	if (!drm_dev_enter(pipe->crtc.dev, &idx))
45*50848e37SDaniel Mack 		return;
46*50848e37SDaniel Mack 
47*50848e37SDaniel Mack 	DRM_DEBUG_KMS("\n");
48*50848e37SDaniel Mack 
49*50848e37SDaniel Mack 	ret = mipi_dbi_poweron_conditional_reset(dbidev);
50*50848e37SDaniel Mack 	if (ret < 0)
51*50848e37SDaniel Mack 		goto out_exit;
52*50848e37SDaniel Mack 	if (ret == 1)
53*50848e37SDaniel Mack 		goto out_enable;
54*50848e37SDaniel Mack 
55*50848e37SDaniel Mack 	/* Gamma */
56*50848e37SDaniel Mack 	mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, 0x04);
57*50848e37SDaniel Mack 	mipi_dbi_command(dbi, ILI9163_EN3GAM, 0x00);
58*50848e37SDaniel Mack 
59*50848e37SDaniel Mack 	/* Frame Rate */
60*50848e37SDaniel Mack 	mipi_dbi_command(dbi, ILI9163_FRMCTR1, 0x0a, 0x14);
61*50848e37SDaniel Mack 
62*50848e37SDaniel Mack 	/* Power Control */
63*50848e37SDaniel Mack 	mipi_dbi_command(dbi, ILI9163_PWCTRL1, 0x0a, 0x00);
64*50848e37SDaniel Mack 	mipi_dbi_command(dbi, ILI9163_PWCTRL2, 0x02);
65*50848e37SDaniel Mack 
66*50848e37SDaniel Mack 	/* VCOM */
67*50848e37SDaniel Mack 	mipi_dbi_command(dbi, ILI9163_VMCTRL1, 0x2f, 0x3e);
68*50848e37SDaniel Mack 	mipi_dbi_command(dbi, ILI9163_VMCTRL2, 0x40);
69*50848e37SDaniel Mack 
70*50848e37SDaniel Mack 	/* Memory Access Control */
71*50848e37SDaniel Mack 	mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
72*50848e37SDaniel Mack 
73*50848e37SDaniel Mack 	mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
74*50848e37SDaniel Mack 	msleep(100);
75*50848e37SDaniel Mack 
76*50848e37SDaniel Mack 	mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
77*50848e37SDaniel Mack 	msleep(100);
78*50848e37SDaniel Mack 
79*50848e37SDaniel Mack out_enable:
80*50848e37SDaniel Mack 	switch (dbidev->rotation) {
81*50848e37SDaniel Mack 	default:
82*50848e37SDaniel Mack 		addr_mode = ILI9163_MADCTL_MX | ILI9163_MADCTL_MY;
83*50848e37SDaniel Mack 		break;
84*50848e37SDaniel Mack 	case 90:
85*50848e37SDaniel Mack 		addr_mode = ILI9163_MADCTL_MX | ILI9163_MADCTL_MV;
86*50848e37SDaniel Mack 		break;
87*50848e37SDaniel Mack 	case 180:
88*50848e37SDaniel Mack 		addr_mode = 0;
89*50848e37SDaniel Mack 		break;
90*50848e37SDaniel Mack 	case 270:
91*50848e37SDaniel Mack 		addr_mode = ILI9163_MADCTL_MY | ILI9163_MADCTL_MV;
92*50848e37SDaniel Mack 		break;
93*50848e37SDaniel Mack 	}
94*50848e37SDaniel Mack 	addr_mode |= ILI9163_MADCTL_BGR;
95*50848e37SDaniel Mack 	mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
96*50848e37SDaniel Mack 	mipi_dbi_enable_flush(dbidev, crtc_state, plane_state);
97*50848e37SDaniel Mack out_exit:
98*50848e37SDaniel Mack 	drm_dev_exit(idx);
99*50848e37SDaniel Mack }
100*50848e37SDaniel Mack 
101*50848e37SDaniel Mack static const struct drm_simple_display_pipe_funcs ili9163_pipe_funcs = {
102*50848e37SDaniel Mack 	.enable = yx240qv29_enable,
103*50848e37SDaniel Mack 	.disable = mipi_dbi_pipe_disable,
104*50848e37SDaniel Mack 	.update = mipi_dbi_pipe_update,
105*50848e37SDaniel Mack 	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
106*50848e37SDaniel Mack };
107*50848e37SDaniel Mack 
108*50848e37SDaniel Mack static const struct drm_display_mode yx240qv29_mode = {
109*50848e37SDaniel Mack 	DRM_SIMPLE_MODE(128, 160, 28, 35),
110*50848e37SDaniel Mack };
111*50848e37SDaniel Mack 
112*50848e37SDaniel Mack DEFINE_DRM_GEM_CMA_FOPS(ili9163_fops);
113*50848e37SDaniel Mack 
114*50848e37SDaniel Mack static struct drm_driver ili9163_driver = {
115*50848e37SDaniel Mack 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
116*50848e37SDaniel Mack 	.fops			= &ili9163_fops,
117*50848e37SDaniel Mack 	DRM_GEM_CMA_DRIVER_OPS_VMAP,
118*50848e37SDaniel Mack 	.debugfs_init		= mipi_dbi_debugfs_init,
119*50848e37SDaniel Mack 	.name			= "ili9163",
120*50848e37SDaniel Mack 	.desc			= "Ilitek ILI9163",
121*50848e37SDaniel Mack 	.date			= "20210208",
122*50848e37SDaniel Mack 	.major			= 1,
123*50848e37SDaniel Mack 	.minor			= 0,
124*50848e37SDaniel Mack };
125*50848e37SDaniel Mack 
126*50848e37SDaniel Mack static const struct of_device_id ili9163_of_match[] = {
127*50848e37SDaniel Mack 	{ .compatible = "newhaven,1.8-128160EF" },
128*50848e37SDaniel Mack 	{ }
129*50848e37SDaniel Mack };
130*50848e37SDaniel Mack MODULE_DEVICE_TABLE(of, ili9163_of_match);
131*50848e37SDaniel Mack 
132*50848e37SDaniel Mack static const struct spi_device_id ili9163_id[] = {
133*50848e37SDaniel Mack 	{ "nhd-1.8-128160EF", 0 },
134*50848e37SDaniel Mack 	{ }
135*50848e37SDaniel Mack };
136*50848e37SDaniel Mack MODULE_DEVICE_TABLE(spi, ili9163_id);
137*50848e37SDaniel Mack 
138*50848e37SDaniel Mack static int ili9163_probe(struct spi_device *spi)
139*50848e37SDaniel Mack {
140*50848e37SDaniel Mack 	struct device *dev = &spi->dev;
141*50848e37SDaniel Mack 	struct mipi_dbi_dev *dbidev;
142*50848e37SDaniel Mack 	struct drm_device *drm;
143*50848e37SDaniel Mack 	struct mipi_dbi *dbi;
144*50848e37SDaniel Mack 	struct gpio_desc *dc;
145*50848e37SDaniel Mack 	u32 rotation = 0;
146*50848e37SDaniel Mack 	int ret;
147*50848e37SDaniel Mack 
148*50848e37SDaniel Mack 	dbidev = devm_drm_dev_alloc(dev, &ili9163_driver,
149*50848e37SDaniel Mack 				    struct mipi_dbi_dev, drm);
150*50848e37SDaniel Mack 	if (IS_ERR(dbidev))
151*50848e37SDaniel Mack 		return PTR_ERR(dbidev);
152*50848e37SDaniel Mack 
153*50848e37SDaniel Mack 	dbi = &dbidev->dbi;
154*50848e37SDaniel Mack 	drm = &dbidev->drm;
155*50848e37SDaniel Mack 
156*50848e37SDaniel Mack 	spi_set_drvdata(spi, drm);
157*50848e37SDaniel Mack 
158*50848e37SDaniel Mack 	dbi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
159*50848e37SDaniel Mack 	if (IS_ERR(dbi->reset)) {
160*50848e37SDaniel Mack 		DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
161*50848e37SDaniel Mack 		return PTR_ERR(dbi->reset);
162*50848e37SDaniel Mack 	}
163*50848e37SDaniel Mack 
164*50848e37SDaniel Mack 	dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW);
165*50848e37SDaniel Mack 	if (IS_ERR(dc)) {
166*50848e37SDaniel Mack 		DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n");
167*50848e37SDaniel Mack 		return PTR_ERR(dc);
168*50848e37SDaniel Mack 	}
169*50848e37SDaniel Mack 
170*50848e37SDaniel Mack 	dbidev->backlight = devm_of_find_backlight(dev);
171*50848e37SDaniel Mack 	if (IS_ERR(dbidev->backlight))
172*50848e37SDaniel Mack 		return PTR_ERR(dbidev->backlight);
173*50848e37SDaniel Mack 
174*50848e37SDaniel Mack 	device_property_read_u32(dev, "rotation", &rotation);
175*50848e37SDaniel Mack 
176*50848e37SDaniel Mack 	ret = mipi_dbi_spi_init(spi, dbi, dc);
177*50848e37SDaniel Mack 	if (ret)
178*50848e37SDaniel Mack 		return ret;
179*50848e37SDaniel Mack 
180*50848e37SDaniel Mack 	ret = mipi_dbi_dev_init(dbidev, &ili9163_pipe_funcs, &yx240qv29_mode, rotation);
181*50848e37SDaniel Mack 	if (ret)
182*50848e37SDaniel Mack 		return ret;
183*50848e37SDaniel Mack 
184*50848e37SDaniel Mack 	drm_mode_config_reset(drm);
185*50848e37SDaniel Mack 
186*50848e37SDaniel Mack 	ret = drm_dev_register(drm, 0);
187*50848e37SDaniel Mack 	if (ret)
188*50848e37SDaniel Mack 		return ret;
189*50848e37SDaniel Mack 
190*50848e37SDaniel Mack 	drm_fbdev_generic_setup(drm, 0);
191*50848e37SDaniel Mack 
192*50848e37SDaniel Mack 	return 0;
193*50848e37SDaniel Mack }
194*50848e37SDaniel Mack 
195*50848e37SDaniel Mack static int ili9163_remove(struct spi_device *spi)
196*50848e37SDaniel Mack {
197*50848e37SDaniel Mack 	struct drm_device *drm = spi_get_drvdata(spi);
198*50848e37SDaniel Mack 
199*50848e37SDaniel Mack 	drm_dev_unplug(drm);
200*50848e37SDaniel Mack 	drm_atomic_helper_shutdown(drm);
201*50848e37SDaniel Mack 
202*50848e37SDaniel Mack 	return 0;
203*50848e37SDaniel Mack }
204*50848e37SDaniel Mack 
205*50848e37SDaniel Mack static void ili9163_shutdown(struct spi_device *spi)
206*50848e37SDaniel Mack {
207*50848e37SDaniel Mack 	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
208*50848e37SDaniel Mack }
209*50848e37SDaniel Mack 
210*50848e37SDaniel Mack static struct spi_driver ili9163_spi_driver = {
211*50848e37SDaniel Mack 	.driver = {
212*50848e37SDaniel Mack 		.name = "ili9163",
213*50848e37SDaniel Mack 		.of_match_table = ili9163_of_match,
214*50848e37SDaniel Mack 	},
215*50848e37SDaniel Mack 	.id_table = ili9163_id,
216*50848e37SDaniel Mack 	.probe = ili9163_probe,
217*50848e37SDaniel Mack 	.remove = ili9163_remove,
218*50848e37SDaniel Mack 	.shutdown = ili9163_shutdown,
219*50848e37SDaniel Mack };
220*50848e37SDaniel Mack module_spi_driver(ili9163_spi_driver);
221*50848e37SDaniel Mack 
222*50848e37SDaniel Mack MODULE_DESCRIPTION("Ilitek ILI9163 DRM driver");
223*50848e37SDaniel Mack MODULE_AUTHOR("Daniel Mack <daniel@zonque.org>");
224*50848e37SDaniel Mack MODULE_LICENSE("GPL");
225