xref: /linux/drivers/gpu/drm/sitronix/st7571-i2c.c (revision 9b8f32002cddf792fc6657c6f084585c7af1a858)
1*9b8f3200SMarcus Folkesson // SPDX-License-Identifier: GPL-2.0-or-later
2*9b8f3200SMarcus Folkesson /*
3*9b8f3200SMarcus Folkesson  * Driver for Sitronix ST7571, a 4 level gray scale dot matrix LCD controller
4*9b8f3200SMarcus Folkesson  *
5*9b8f3200SMarcus Folkesson  * Copyright (C) 2025 Marcus Folkesson <marcus.folkesson@gmail.com>
6*9b8f3200SMarcus Folkesson  */
7*9b8f3200SMarcus Folkesson 
8*9b8f3200SMarcus Folkesson #include <linux/bitfield.h>
9*9b8f3200SMarcus Folkesson #include <linux/delay.h>
10*9b8f3200SMarcus Folkesson #include <linux/gpio/consumer.h>
11*9b8f3200SMarcus Folkesson #include <linux/i2c.h>
12*9b8f3200SMarcus Folkesson #include <linux/module.h>
13*9b8f3200SMarcus Folkesson #include <linux/regmap.h>
14*9b8f3200SMarcus Folkesson 
15*9b8f3200SMarcus Folkesson #include <drm/clients/drm_client_setup.h>
16*9b8f3200SMarcus Folkesson #include <drm/drm_atomic.h>
17*9b8f3200SMarcus Folkesson #include <drm/drm_atomic_helper.h>
18*9b8f3200SMarcus Folkesson #include <drm/drm_connector.h>
19*9b8f3200SMarcus Folkesson #include <drm/drm_crtc_helper.h>
20*9b8f3200SMarcus Folkesson #include <drm/drm_damage_helper.h>
21*9b8f3200SMarcus Folkesson #include <drm/drm_drv.h>
22*9b8f3200SMarcus Folkesson #include <drm/drm_encoder.h>
23*9b8f3200SMarcus Folkesson #include <drm/drm_fb_helper.h>
24*9b8f3200SMarcus Folkesson #include <drm/drm_fbdev_shmem.h>
25*9b8f3200SMarcus Folkesson #include <drm/drm_fourcc.h>
26*9b8f3200SMarcus Folkesson #include <drm/drm_framebuffer.h>
27*9b8f3200SMarcus Folkesson #include <drm/drm_gem_atomic_helper.h>
28*9b8f3200SMarcus Folkesson #include <drm/drm_gem_framebuffer_helper.h>
29*9b8f3200SMarcus Folkesson #include <drm/drm_gem_shmem_helper.h>
30*9b8f3200SMarcus Folkesson #include <drm/drm_modeset_helper_vtables.h>
31*9b8f3200SMarcus Folkesson #include <drm/drm_module.h>
32*9b8f3200SMarcus Folkesson #include <drm/drm_plane.h>
33*9b8f3200SMarcus Folkesson #include <drm/drm_probe_helper.h>
34*9b8f3200SMarcus Folkesson 
35*9b8f3200SMarcus Folkesson #include <video/display_timing.h>
36*9b8f3200SMarcus Folkesson #include <video/of_display_timing.h>
37*9b8f3200SMarcus Folkesson 
38*9b8f3200SMarcus Folkesson #define ST7571_COMMAND_MODE			(0x00)
39*9b8f3200SMarcus Folkesson #define ST7571_DATA_MODE			(0x40)
40*9b8f3200SMarcus Folkesson 
41*9b8f3200SMarcus Folkesson /* Normal mode command set */
42*9b8f3200SMarcus Folkesson #define ST7571_DISPLAY_OFF			(0xae)
43*9b8f3200SMarcus Folkesson #define ST7571_DISPLAY_ON			(0xaf)
44*9b8f3200SMarcus Folkesson #define ST7571_OSC_ON				(0xab)
45*9b8f3200SMarcus Folkesson #define ST7571_SET_COLUMN_LSB(c)		(0x00 | FIELD_PREP(GENMASK(3, 0), (c)))
46*9b8f3200SMarcus Folkesson #define ST7571_SET_COLUMN_MSB(c)		(0x10 | FIELD_PREP(GENMASK(2, 0), (c) >> 4))
47*9b8f3200SMarcus Folkesson #define ST7571_SET_COM0_LSB(x)			(FIELD_PREP(GENMASK(6, 0), (x)))
48*9b8f3200SMarcus Folkesson #define ST7571_SET_COM0_MSB			(0x44)
49*9b8f3200SMarcus Folkesson #define ST7571_SET_COM_SCAN_DIR(d)		(0xc0 | FIELD_PREP(GENMASK(3, 3), (d)))
50*9b8f3200SMarcus Folkesson #define ST7571_SET_CONTRAST_LSB(c)		(FIELD_PREP(GENMASK(5, 0), (c)))
51*9b8f3200SMarcus Folkesson #define ST7571_SET_CONTRAST_MSB			(0x81)
52*9b8f3200SMarcus Folkesson #define ST7571_SET_DISPLAY_DUTY_LSB(d)		(FIELD_PREP(GENMASK(7, 0), (d)))
53*9b8f3200SMarcus Folkesson #define ST7571_SET_DISPLAY_DUTY_MSB		(0x48)
54*9b8f3200SMarcus Folkesson #define ST7571_SET_ENTIRE_DISPLAY_ON(p)		(0xa4 | FIELD_PREP(GENMASK(0, 0), (p)))
55*9b8f3200SMarcus Folkesson #define ST7571_SET_LCD_BIAS(b)			(0x50 | FIELD_PREP(GENMASK(2, 0), (b)))
56*9b8f3200SMarcus Folkesson #define ST7571_SET_MODE_LSB(m)			(FIELD_PREP(GENMASK(7, 2), (m)))
57*9b8f3200SMarcus Folkesson #define ST7571_SET_MODE_MSB			(0x38)
58*9b8f3200SMarcus Folkesson #define ST7571_SET_PAGE(p)			(0xb0 | FIELD_PREP(GENMASK(3, 0), (p)))
59*9b8f3200SMarcus Folkesson #define ST7571_SET_POWER(p)			(0x28 | FIELD_PREP(GENMASK(2, 0), (p)))
60*9b8f3200SMarcus Folkesson #define ST7571_SET_REGULATOR_REG(r)		(0x20 | FIELD_PREP(GENMASK(2, 0), (r)))
61*9b8f3200SMarcus Folkesson #define ST7571_SET_REVERSE(r)			(0xa6 | FIELD_PREP(GENMASK(0, 0), (r)))
62*9b8f3200SMarcus Folkesson #define ST7571_SET_SEG_SCAN_DIR(d)		(0xa0 | FIELD_PREP(GENMASK(0, 0), (d)))
63*9b8f3200SMarcus Folkesson #define ST7571_SET_START_LINE_LSB(l)		(FIELD_PREP(GENMASK(6, 0), (l)))
64*9b8f3200SMarcus Folkesson #define ST7571_SET_START_LINE_MSB		(0x40)
65*9b8f3200SMarcus Folkesson 
66*9b8f3200SMarcus Folkesson /* Extension command set 3 */
67*9b8f3200SMarcus Folkesson #define ST7571_COMMAND_SET_3			(0x7b)
68*9b8f3200SMarcus Folkesson #define ST7571_SET_COLOR_MODE(c)		(0x10 | FIELD_PREP(GENMASK(0, 0), (c)))
69*9b8f3200SMarcus Folkesson #define ST7571_COMMAND_SET_NORMAL		(0x00)
70*9b8f3200SMarcus Folkesson 
71*9b8f3200SMarcus Folkesson #define ST7571_PAGE_HEIGHT 8
72*9b8f3200SMarcus Folkesson 
73*9b8f3200SMarcus Folkesson #define DRIVER_NAME "st7571"
74*9b8f3200SMarcus Folkesson #define DRIVER_DESC "ST7571 DRM driver"
75*9b8f3200SMarcus Folkesson #define DRIVER_MAJOR 1
76*9b8f3200SMarcus Folkesson #define DRIVER_MINOR 0
77*9b8f3200SMarcus Folkesson 
78*9b8f3200SMarcus Folkesson enum st7571_color_mode {
79*9b8f3200SMarcus Folkesson 	ST7571_COLOR_MODE_GRAY = 0,
80*9b8f3200SMarcus Folkesson 	ST7571_COLOR_MODE_BLACKWHITE = 1,
81*9b8f3200SMarcus Folkesson };
82*9b8f3200SMarcus Folkesson 
83*9b8f3200SMarcus Folkesson struct st7571_device;
84*9b8f3200SMarcus Folkesson 
85*9b8f3200SMarcus Folkesson struct st7571_panel_constraints {
86*9b8f3200SMarcus Folkesson 	u32 min_nlines;
87*9b8f3200SMarcus Folkesson 	u32 max_nlines;
88*9b8f3200SMarcus Folkesson 	u32 min_ncols;
89*9b8f3200SMarcus Folkesson 	u32 max_ncols;
90*9b8f3200SMarcus Folkesson 	bool support_grayscale;
91*9b8f3200SMarcus Folkesson };
92*9b8f3200SMarcus Folkesson 
93*9b8f3200SMarcus Folkesson struct st7571_panel_data {
94*9b8f3200SMarcus Folkesson 	int (*init)(struct st7571_device *st7571);
95*9b8f3200SMarcus Folkesson 	struct st7571_panel_constraints constraints;
96*9b8f3200SMarcus Folkesson };
97*9b8f3200SMarcus Folkesson 
98*9b8f3200SMarcus Folkesson struct st7571_panel_format {
99*9b8f3200SMarcus Folkesson 	void (*prepare_buffer)(struct st7571_device *st7571,
100*9b8f3200SMarcus Folkesson 			       const struct iosys_map *vmap,
101*9b8f3200SMarcus Folkesson 			       struct drm_framebuffer *fb,
102*9b8f3200SMarcus Folkesson 			       struct drm_rect *rect,
103*9b8f3200SMarcus Folkesson 			       struct drm_format_conv_state *fmtcnv_state);
104*9b8f3200SMarcus Folkesson 	int (*update_rect)(struct drm_framebuffer *fb, struct drm_rect *rect);
105*9b8f3200SMarcus Folkesson 	enum st7571_color_mode mode;
106*9b8f3200SMarcus Folkesson 	const u8 nformats;
107*9b8f3200SMarcus Folkesson 	const u32 formats[];
108*9b8f3200SMarcus Folkesson };
109*9b8f3200SMarcus Folkesson 
110*9b8f3200SMarcus Folkesson struct st7571_device {
111*9b8f3200SMarcus Folkesson 	struct drm_device dev;
112*9b8f3200SMarcus Folkesson 
113*9b8f3200SMarcus Folkesson 	struct drm_plane primary_plane;
114*9b8f3200SMarcus Folkesson 	struct drm_crtc crtc;
115*9b8f3200SMarcus Folkesson 	struct drm_encoder encoder;
116*9b8f3200SMarcus Folkesson 	struct drm_connector connector;
117*9b8f3200SMarcus Folkesson 
118*9b8f3200SMarcus Folkesson 	struct drm_display_mode mode;
119*9b8f3200SMarcus Folkesson 
120*9b8f3200SMarcus Folkesson 	const struct st7571_panel_format *pformat;
121*9b8f3200SMarcus Folkesson 	const struct st7571_panel_data *pdata;
122*9b8f3200SMarcus Folkesson 	struct i2c_client *client;
123*9b8f3200SMarcus Folkesson 	struct gpio_desc *reset;
124*9b8f3200SMarcus Folkesson 	struct regmap *regmap;
125*9b8f3200SMarcus Folkesson 
126*9b8f3200SMarcus Folkesson 	/*
127*9b8f3200SMarcus Folkesson 	 * Depending on the hardware design, the acknowledge signal may be hard to
128*9b8f3200SMarcus Folkesson 	 * recognize as a valid logic "0" level.
129*9b8f3200SMarcus Folkesson 	 * Therefor, ignore NAK if possible to stay compatible with most hardware designs
130*9b8f3200SMarcus Folkesson 	 * and off-the-shelf panels out there.
131*9b8f3200SMarcus Folkesson 	 *
132*9b8f3200SMarcus Folkesson 	 * From section 6.4 MICROPOCESSOR INTERFACE section in the datasheet:
133*9b8f3200SMarcus Folkesson 	 *
134*9b8f3200SMarcus Folkesson 	 * "By connecting SDA_OUT to SDA_IN externally, the SDA line becomes fully
135*9b8f3200SMarcus Folkesson 	 * I2C interface compatible.
136*9b8f3200SMarcus Folkesson 	 * Separating acknowledge-output from serial data
137*9b8f3200SMarcus Folkesson 	 * input is advantageous for chip-on-glass (COG) applications. In COG
138*9b8f3200SMarcus Folkesson 	 * applications, the ITO resistance and the pull-up resistor will form a
139*9b8f3200SMarcus Folkesson 	 * voltage  divider, which affects acknowledge-signal level. Larger ITO
140*9b8f3200SMarcus Folkesson 	 * resistance will raise the acknowledged-signal level and system cannot
141*9b8f3200SMarcus Folkesson 	 * recognize this level as a valid logic “0” level. By separating SDA_IN from
142*9b8f3200SMarcus Folkesson 	 * SDA_OUT, the IC can be used in a mode that ignores the acknowledge-bit.
143*9b8f3200SMarcus Folkesson 	 * For applications which check acknowledge-bit, it is necessary to minimize
144*9b8f3200SMarcus Folkesson 	 * the ITO resistance of the SDA_OUT trace to guarantee a valid low level."
145*9b8f3200SMarcus Folkesson 	 *
146*9b8f3200SMarcus Folkesson 	 */
147*9b8f3200SMarcus Folkesson 	bool ignore_nak;
148*9b8f3200SMarcus Folkesson 
149*9b8f3200SMarcus Folkesson 	bool grayscale;
150*9b8f3200SMarcus Folkesson 	u32 height_mm;
151*9b8f3200SMarcus Folkesson 	u32 width_mm;
152*9b8f3200SMarcus Folkesson 	u32 startline;
153*9b8f3200SMarcus Folkesson 	u32 nlines;
154*9b8f3200SMarcus Folkesson 	u32 ncols;
155*9b8f3200SMarcus Folkesson 	u32 bpp;
156*9b8f3200SMarcus Folkesson 
157*9b8f3200SMarcus Folkesson 	/* Intermediate buffer in LCD friendly format */
158*9b8f3200SMarcus Folkesson 	u8 *hwbuf;
159*9b8f3200SMarcus Folkesson 
160*9b8f3200SMarcus Folkesson 	/* Row of (transformed) pixels ready to be written to the display */
161*9b8f3200SMarcus Folkesson 	u8 *row;
162*9b8f3200SMarcus Folkesson };
163*9b8f3200SMarcus Folkesson 
164*9b8f3200SMarcus Folkesson static inline struct st7571_device *drm_to_st7571(struct drm_device *dev)
165*9b8f3200SMarcus Folkesson {
166*9b8f3200SMarcus Folkesson 	return container_of(dev, struct st7571_device, dev);
167*9b8f3200SMarcus Folkesson }
168*9b8f3200SMarcus Folkesson 
169*9b8f3200SMarcus Folkesson static int st7571_regmap_write(void *context, const void *data, size_t count)
170*9b8f3200SMarcus Folkesson {
171*9b8f3200SMarcus Folkesson 	struct i2c_client *client = context;
172*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = i2c_get_clientdata(client);
173*9b8f3200SMarcus Folkesson 	int ret;
174*9b8f3200SMarcus Folkesson 
175*9b8f3200SMarcus Folkesson 	struct i2c_msg msg = {
176*9b8f3200SMarcus Folkesson 		.addr = st7571->client->addr,
177*9b8f3200SMarcus Folkesson 		.flags = st7571->ignore_nak ? I2C_M_IGNORE_NAK : 0,
178*9b8f3200SMarcus Folkesson 		.len = count,
179*9b8f3200SMarcus Folkesson 		.buf = (u8 *)data
180*9b8f3200SMarcus Folkesson 	};
181*9b8f3200SMarcus Folkesson 
182*9b8f3200SMarcus Folkesson 	ret = i2c_transfer(st7571->client->adapter, &msg, 1);
183*9b8f3200SMarcus Folkesson 
184*9b8f3200SMarcus Folkesson 	/*
185*9b8f3200SMarcus Folkesson 	 * Unfortunately, there is no way to check if the transfer failed because of
186*9b8f3200SMarcus Folkesson 	 * a NAK or something else as I2C bus drivers use different return values for NAK.
187*9b8f3200SMarcus Folkesson 	 *
188*9b8f3200SMarcus Folkesson 	 * However, if the transfer fails and ignore_nak is set, we know it is an error.
189*9b8f3200SMarcus Folkesson 	 */
190*9b8f3200SMarcus Folkesson 	if (ret < 0 && st7571->ignore_nak)
191*9b8f3200SMarcus Folkesson 		return ret;
192*9b8f3200SMarcus Folkesson 
193*9b8f3200SMarcus Folkesson 	return 0;
194*9b8f3200SMarcus Folkesson }
195*9b8f3200SMarcus Folkesson 
196*9b8f3200SMarcus Folkesson /* The st7571 driver does not read registers but regmap expects a .read */
197*9b8f3200SMarcus Folkesson static int st7571_regmap_read(void *context, const void *reg_buf,
198*9b8f3200SMarcus Folkesson 			      size_t reg_size, void *val_buf, size_t val_size)
199*9b8f3200SMarcus Folkesson {
200*9b8f3200SMarcus Folkesson 	return -EOPNOTSUPP;
201*9b8f3200SMarcus Folkesson }
202*9b8f3200SMarcus Folkesson 
203*9b8f3200SMarcus Folkesson static int st7571_send_command_list(struct st7571_device *st7571,
204*9b8f3200SMarcus Folkesson 				    const u8 *cmd_list, size_t len)
205*9b8f3200SMarcus Folkesson {
206*9b8f3200SMarcus Folkesson 	int ret;
207*9b8f3200SMarcus Folkesson 
208*9b8f3200SMarcus Folkesson 	for (int i = 0; i < len; i++) {
209*9b8f3200SMarcus Folkesson 		ret = regmap_write(st7571->regmap, ST7571_COMMAND_MODE, cmd_list[i]);
210*9b8f3200SMarcus Folkesson 		if (ret < 0)
211*9b8f3200SMarcus Folkesson 			return ret;
212*9b8f3200SMarcus Folkesson 	}
213*9b8f3200SMarcus Folkesson 
214*9b8f3200SMarcus Folkesson 	return ret;
215*9b8f3200SMarcus Folkesson }
216*9b8f3200SMarcus Folkesson 
217*9b8f3200SMarcus Folkesson static inline u8 st7571_transform_xy(const char *p, int x, int y)
218*9b8f3200SMarcus Folkesson {
219*9b8f3200SMarcus Folkesson 	int xrest = x % 8;
220*9b8f3200SMarcus Folkesson 	u8 result = 0;
221*9b8f3200SMarcus Folkesson 
222*9b8f3200SMarcus Folkesson 	/*
223*9b8f3200SMarcus Folkesson 	 * Transforms an (x, y) pixel coordinate into a vertical 8-bit
224*9b8f3200SMarcus Folkesson 	 * column from the framebuffer. It calculates the corresponding byte in the
225*9b8f3200SMarcus Folkesson 	 * framebuffer, extracts the bit at the given x position across 8 consecutive
226*9b8f3200SMarcus Folkesson 	 * rows, and packs those bits into a single byte.
227*9b8f3200SMarcus Folkesson 	 *
228*9b8f3200SMarcus Folkesson 	 * Return an 8-bit value representing a vertical column of pixels.
229*9b8f3200SMarcus Folkesson 	 */
230*9b8f3200SMarcus Folkesson 	x = x / 8;
231*9b8f3200SMarcus Folkesson 	y = (y / 8) * 8;
232*9b8f3200SMarcus Folkesson 
233*9b8f3200SMarcus Folkesson 	for (int i = 0; i < 8; i++) {
234*9b8f3200SMarcus Folkesson 		int row_idx = y + i;
235*9b8f3200SMarcus Folkesson 		u8 byte = p[row_idx * 16 + x];
236*9b8f3200SMarcus Folkesson 		u8 bit = (byte >> xrest) & 1;
237*9b8f3200SMarcus Folkesson 
238*9b8f3200SMarcus Folkesson 		result |= (bit << i);
239*9b8f3200SMarcus Folkesson 	}
240*9b8f3200SMarcus Folkesson 
241*9b8f3200SMarcus Folkesson 	return result;
242*9b8f3200SMarcus Folkesson }
243*9b8f3200SMarcus Folkesson 
244*9b8f3200SMarcus Folkesson static int st7571_set_position(struct st7571_device *st7571, int x, int y)
245*9b8f3200SMarcus Folkesson {
246*9b8f3200SMarcus Folkesson 	u8 cmd_list[] = {
247*9b8f3200SMarcus Folkesson 		ST7571_SET_COLUMN_LSB(x),
248*9b8f3200SMarcus Folkesson 		ST7571_SET_COLUMN_MSB(x),
249*9b8f3200SMarcus Folkesson 		ST7571_SET_PAGE(y / ST7571_PAGE_HEIGHT),
250*9b8f3200SMarcus Folkesson 	};
251*9b8f3200SMarcus Folkesson 
252*9b8f3200SMarcus Folkesson 	return st7571_send_command_list(st7571, cmd_list, ARRAY_SIZE(cmd_list));
253*9b8f3200SMarcus Folkesson }
254*9b8f3200SMarcus Folkesson 
255*9b8f3200SMarcus Folkesson static int st7571_fb_clear_screen(struct st7571_device *st7571)
256*9b8f3200SMarcus Folkesson {
257*9b8f3200SMarcus Folkesson 	u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp;
258*9b8f3200SMarcus Folkesson 	char pixelvalue = 0x00;
259*9b8f3200SMarcus Folkesson 
260*9b8f3200SMarcus Folkesson 	for (int i = 0; i < npixels; i++)
261*9b8f3200SMarcus Folkesson 		regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1);
262*9b8f3200SMarcus Folkesson 
263*9b8f3200SMarcus Folkesson 	return 0;
264*9b8f3200SMarcus Folkesson }
265*9b8f3200SMarcus Folkesson 
266*9b8f3200SMarcus Folkesson static void st7571_prepare_buffer_monochrome(struct st7571_device *st7571,
267*9b8f3200SMarcus Folkesson 					     const struct iosys_map *vmap,
268*9b8f3200SMarcus Folkesson 					     struct drm_framebuffer *fb,
269*9b8f3200SMarcus Folkesson 					     struct drm_rect *rect,
270*9b8f3200SMarcus Folkesson 					     struct drm_format_conv_state *fmtcnv_state)
271*9b8f3200SMarcus Folkesson {
272*9b8f3200SMarcus Folkesson 	unsigned int dst_pitch;
273*9b8f3200SMarcus Folkesson 	struct iosys_map dst;
274*9b8f3200SMarcus Folkesson 	u32 size;
275*9b8f3200SMarcus Folkesson 
276*9b8f3200SMarcus Folkesson 	switch (fb->format->format) {
277*9b8f3200SMarcus Folkesson 	case DRM_FORMAT_XRGB8888:
278*9b8f3200SMarcus Folkesson 		dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
279*9b8f3200SMarcus Folkesson 		iosys_map_set_vaddr(&dst, st7571->hwbuf);
280*9b8f3200SMarcus Folkesson 
281*9b8f3200SMarcus Folkesson 		drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
282*9b8f3200SMarcus Folkesson 		break;
283*9b8f3200SMarcus Folkesson 
284*9b8f3200SMarcus Folkesson 	case DRM_FORMAT_R1:
285*9b8f3200SMarcus Folkesson 		size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
286*9b8f3200SMarcus Folkesson 		memcpy(st7571->hwbuf, vmap->vaddr, size);
287*9b8f3200SMarcus Folkesson 		break;
288*9b8f3200SMarcus Folkesson 	}
289*9b8f3200SMarcus Folkesson }
290*9b8f3200SMarcus Folkesson 
291*9b8f3200SMarcus Folkesson static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571,
292*9b8f3200SMarcus Folkesson 					    const struct iosys_map *vmap,
293*9b8f3200SMarcus Folkesson 					    struct drm_framebuffer *fb,
294*9b8f3200SMarcus Folkesson 					    struct drm_rect *rect,
295*9b8f3200SMarcus Folkesson 					    struct drm_format_conv_state *fmtcnv_state)
296*9b8f3200SMarcus Folkesson {
297*9b8f3200SMarcus Folkesson 	u32 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
298*9b8f3200SMarcus Folkesson 	unsigned int dst_pitch;
299*9b8f3200SMarcus Folkesson 	struct iosys_map dst;
300*9b8f3200SMarcus Folkesson 
301*9b8f3200SMarcus Folkesson 	switch (fb->format->format) {
302*9b8f3200SMarcus Folkesson 	case DRM_FORMAT_XRGB8888: /* Only support XRGB8888 in monochrome mode */
303*9b8f3200SMarcus Folkesson 		dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
304*9b8f3200SMarcus Folkesson 		iosys_map_set_vaddr(&dst, st7571->hwbuf);
305*9b8f3200SMarcus Folkesson 
306*9b8f3200SMarcus Folkesson 		drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
307*9b8f3200SMarcus Folkesson 		break;
308*9b8f3200SMarcus Folkesson 
309*9b8f3200SMarcus Folkesson 	case DRM_FORMAT_R1:
310*9b8f3200SMarcus Folkesson 		size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 8;
311*9b8f3200SMarcus Folkesson 		memcpy(st7571->hwbuf, vmap->vaddr, size);
312*9b8f3200SMarcus Folkesson 		break;
313*9b8f3200SMarcus Folkesson 
314*9b8f3200SMarcus Folkesson 	case DRM_FORMAT_R2:
315*9b8f3200SMarcus Folkesson 		size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4;
316*9b8f3200SMarcus Folkesson 		memcpy(st7571->hwbuf, vmap->vaddr, size);
317*9b8f3200SMarcus Folkesson 		break;
318*9b8f3200SMarcus Folkesson 	};
319*9b8f3200SMarcus Folkesson }
320*9b8f3200SMarcus Folkesson 
321*9b8f3200SMarcus Folkesson static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
322*9b8f3200SMarcus Folkesson {
323*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = drm_to_st7571(fb->dev);
324*9b8f3200SMarcus Folkesson 	char *row = st7571->row;
325*9b8f3200SMarcus Folkesson 
326*9b8f3200SMarcus Folkesson 	/* Align y to display page boundaries */
327*9b8f3200SMarcus Folkesson 	rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
328*9b8f3200SMarcus Folkesson 	rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
329*9b8f3200SMarcus Folkesson 
330*9b8f3200SMarcus Folkesson 	for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
331*9b8f3200SMarcus Folkesson 		for (int x = rect->x1; x < rect->x2; x++)
332*9b8f3200SMarcus Folkesson 			row[x] = st7571_transform_xy(st7571->hwbuf, x, y);
333*9b8f3200SMarcus Folkesson 
334*9b8f3200SMarcus Folkesson 		st7571_set_position(st7571, rect->x1, y);
335*9b8f3200SMarcus Folkesson 
336*9b8f3200SMarcus Folkesson 		/* TODO: Investige why we can't write multiple bytes at once */
337*9b8f3200SMarcus Folkesson 		for (int x = rect->x1; x < rect->x2; x++)
338*9b8f3200SMarcus Folkesson 			regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
339*9b8f3200SMarcus Folkesson 	}
340*9b8f3200SMarcus Folkesson 
341*9b8f3200SMarcus Folkesson 	return 0;
342*9b8f3200SMarcus Folkesson }
343*9b8f3200SMarcus Folkesson 
344*9b8f3200SMarcus Folkesson static int st7571_fb_update_rect_grayscale(struct drm_framebuffer *fb, struct drm_rect *rect)
345*9b8f3200SMarcus Folkesson {
346*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = drm_to_st7571(fb->dev);
347*9b8f3200SMarcus Folkesson 	u32 format = fb->format->format;
348*9b8f3200SMarcus Folkesson 	char *row = st7571->row;
349*9b8f3200SMarcus Folkesson 	int x1;
350*9b8f3200SMarcus Folkesson 	int x2;
351*9b8f3200SMarcus Folkesson 
352*9b8f3200SMarcus Folkesson 	/* Align y to display page boundaries */
353*9b8f3200SMarcus Folkesson 	rect->y1 = round_down(rect->y1, ST7571_PAGE_HEIGHT);
354*9b8f3200SMarcus Folkesson 	rect->y2 = min_t(unsigned int, round_up(rect->y2, ST7571_PAGE_HEIGHT), st7571->nlines);
355*9b8f3200SMarcus Folkesson 
356*9b8f3200SMarcus Folkesson 	switch (format) {
357*9b8f3200SMarcus Folkesson 	case DRM_FORMAT_XRGB8888:
358*9b8f3200SMarcus Folkesson 		/* Threated as monochrome (R1) */
359*9b8f3200SMarcus Folkesson 		fallthrough;
360*9b8f3200SMarcus Folkesson 	case DRM_FORMAT_R1:
361*9b8f3200SMarcus Folkesson 		x1 = rect->x1;
362*9b8f3200SMarcus Folkesson 		x2 = rect->x2;
363*9b8f3200SMarcus Folkesson 		break;
364*9b8f3200SMarcus Folkesson 	case DRM_FORMAT_R2:
365*9b8f3200SMarcus Folkesson 		x1 = rect->x1 * 2;
366*9b8f3200SMarcus Folkesson 		x2 = rect->x2 * 2;
367*9b8f3200SMarcus Folkesson 		break;
368*9b8f3200SMarcus Folkesson 	}
369*9b8f3200SMarcus Folkesson 
370*9b8f3200SMarcus Folkesson 	for (int y = rect->y1; y < rect->y2; y += ST7571_PAGE_HEIGHT) {
371*9b8f3200SMarcus Folkesson 		for (int x = x1; x < x2; x++)
372*9b8f3200SMarcus Folkesson 			row[x] = st7571_transform_xy(st7571->hwbuf, x, y);
373*9b8f3200SMarcus Folkesson 
374*9b8f3200SMarcus Folkesson 		st7571_set_position(st7571, rect->x1, y);
375*9b8f3200SMarcus Folkesson 
376*9b8f3200SMarcus Folkesson 		/* TODO: Investige why we can't write multiple bytes at once */
377*9b8f3200SMarcus Folkesson 		for (int x = x1; x < x2; x++) {
378*9b8f3200SMarcus Folkesson 			regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
379*9b8f3200SMarcus Folkesson 
380*9b8f3200SMarcus Folkesson 			/*
381*9b8f3200SMarcus Folkesson 			 * As the display supports grayscale, all pixels must be written as two bits
382*9b8f3200SMarcus Folkesson 			 * even if the format is monochrome.
383*9b8f3200SMarcus Folkesson 			 *
384*9b8f3200SMarcus Folkesson 			 * The bit values maps to the following grayscale:
385*9b8f3200SMarcus Folkesson 			 * 0 0 = White
386*9b8f3200SMarcus Folkesson 			 * 0 1 = Light gray
387*9b8f3200SMarcus Folkesson 			 * 1 0 = Dark gray
388*9b8f3200SMarcus Folkesson 			 * 1 1 = Black
389*9b8f3200SMarcus Folkesson 			 *
390*9b8f3200SMarcus Folkesson 			 * For monochrome formats, write the same value twice to get
391*9b8f3200SMarcus Folkesson 			 * either a black or white pixel.
392*9b8f3200SMarcus Folkesson 			 */
393*9b8f3200SMarcus Folkesson 			if (format == DRM_FORMAT_R1 || format == DRM_FORMAT_XRGB8888)
394*9b8f3200SMarcus Folkesson 				regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, row + x, 1);
395*9b8f3200SMarcus Folkesson 		}
396*9b8f3200SMarcus Folkesson 	}
397*9b8f3200SMarcus Folkesson 
398*9b8f3200SMarcus Folkesson 	return 0;
399*9b8f3200SMarcus Folkesson }
400*9b8f3200SMarcus Folkesson 
401*9b8f3200SMarcus Folkesson static int st7571_connector_get_modes(struct drm_connector *conn)
402*9b8f3200SMarcus Folkesson {
403*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = drm_to_st7571(conn->dev);
404*9b8f3200SMarcus Folkesson 
405*9b8f3200SMarcus Folkesson 	return drm_connector_helper_get_modes_fixed(conn, &st7571->mode);
406*9b8f3200SMarcus Folkesson }
407*9b8f3200SMarcus Folkesson 
408*9b8f3200SMarcus Folkesson static const struct drm_connector_helper_funcs st7571_connector_helper_funcs = {
409*9b8f3200SMarcus Folkesson 	.get_modes = st7571_connector_get_modes,
410*9b8f3200SMarcus Folkesson };
411*9b8f3200SMarcus Folkesson 
412*9b8f3200SMarcus Folkesson static const struct st7571_panel_format st7571_monochrome = {
413*9b8f3200SMarcus Folkesson 	.prepare_buffer = st7571_prepare_buffer_monochrome,
414*9b8f3200SMarcus Folkesson 	.update_rect = st7571_fb_update_rect_monochrome,
415*9b8f3200SMarcus Folkesson 	.mode = ST7571_COLOR_MODE_BLACKWHITE,
416*9b8f3200SMarcus Folkesson 	.formats = {
417*9b8f3200SMarcus Folkesson 		DRM_FORMAT_XRGB8888,
418*9b8f3200SMarcus Folkesson 		DRM_FORMAT_R1,
419*9b8f3200SMarcus Folkesson 	},
420*9b8f3200SMarcus Folkesson 	.nformats = 2,
421*9b8f3200SMarcus Folkesson };
422*9b8f3200SMarcus Folkesson 
423*9b8f3200SMarcus Folkesson static const struct st7571_panel_format st7571_grayscale = {
424*9b8f3200SMarcus Folkesson 	.prepare_buffer = st7571_prepare_buffer_grayscale,
425*9b8f3200SMarcus Folkesson 	.update_rect = st7571_fb_update_rect_grayscale,
426*9b8f3200SMarcus Folkesson 	.mode = ST7571_COLOR_MODE_GRAY,
427*9b8f3200SMarcus Folkesson 	.formats = {
428*9b8f3200SMarcus Folkesson 		DRM_FORMAT_XRGB8888,
429*9b8f3200SMarcus Folkesson 		DRM_FORMAT_R1,
430*9b8f3200SMarcus Folkesson 		DRM_FORMAT_R2,
431*9b8f3200SMarcus Folkesson 	},
432*9b8f3200SMarcus Folkesson 	.nformats = 3,
433*9b8f3200SMarcus Folkesson };
434*9b8f3200SMarcus Folkesson 
435*9b8f3200SMarcus Folkesson static const u64 st7571_primary_plane_fmtmods[] = {
436*9b8f3200SMarcus Folkesson 	DRM_FORMAT_MOD_LINEAR,
437*9b8f3200SMarcus Folkesson 	DRM_FORMAT_MOD_INVALID
438*9b8f3200SMarcus Folkesson };
439*9b8f3200SMarcus Folkesson 
440*9b8f3200SMarcus Folkesson static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane,
441*9b8f3200SMarcus Folkesson 						    struct drm_atomic_state *state)
442*9b8f3200SMarcus Folkesson {
443*9b8f3200SMarcus Folkesson 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
444*9b8f3200SMarcus Folkesson 	struct drm_crtc *new_crtc = new_plane_state->crtc;
445*9b8f3200SMarcus Folkesson 	struct drm_crtc_state *new_crtc_state = NULL;
446*9b8f3200SMarcus Folkesson 
447*9b8f3200SMarcus Folkesson 	if (new_crtc)
448*9b8f3200SMarcus Folkesson 		new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
449*9b8f3200SMarcus Folkesson 
450*9b8f3200SMarcus Folkesson 	return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
451*9b8f3200SMarcus Folkesson 						   DRM_PLANE_NO_SCALING,
452*9b8f3200SMarcus Folkesson 						   DRM_PLANE_NO_SCALING,
453*9b8f3200SMarcus Folkesson 						   false, false);
454*9b8f3200SMarcus Folkesson }
455*9b8f3200SMarcus Folkesson 
456*9b8f3200SMarcus Folkesson static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
457*9b8f3200SMarcus Folkesson 						      struct drm_atomic_state *state)
458*9b8f3200SMarcus Folkesson {
459*9b8f3200SMarcus Folkesson 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
460*9b8f3200SMarcus Folkesson 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
461*9b8f3200SMarcus Folkesson 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
462*9b8f3200SMarcus Folkesson 	struct drm_framebuffer *fb = plane_state->fb;
463*9b8f3200SMarcus Folkesson 	struct drm_atomic_helper_damage_iter iter;
464*9b8f3200SMarcus Folkesson 	struct drm_device *dev = plane->dev;
465*9b8f3200SMarcus Folkesson 	struct drm_rect damage;
466*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = drm_to_st7571(plane->dev);
467*9b8f3200SMarcus Folkesson 	int ret, idx;
468*9b8f3200SMarcus Folkesson 
469*9b8f3200SMarcus Folkesson 	if (!fb)
470*9b8f3200SMarcus Folkesson 		return; /* no framebuffer; plane is disabled */
471*9b8f3200SMarcus Folkesson 
472*9b8f3200SMarcus Folkesson 	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
473*9b8f3200SMarcus Folkesson 	if (ret)
474*9b8f3200SMarcus Folkesson 		return;
475*9b8f3200SMarcus Folkesson 
476*9b8f3200SMarcus Folkesson 	if (!drm_dev_enter(dev, &idx))
477*9b8f3200SMarcus Folkesson 		goto out_drm_gem_fb_end_cpu_access;
478*9b8f3200SMarcus Folkesson 
479*9b8f3200SMarcus Folkesson 	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
480*9b8f3200SMarcus Folkesson 	drm_atomic_for_each_plane_damage(&iter, &damage) {
481*9b8f3200SMarcus Folkesson 		st7571->pformat->prepare_buffer(st7571,
482*9b8f3200SMarcus Folkesson 						&shadow_plane_state->data[0],
483*9b8f3200SMarcus Folkesson 						fb, &damage,
484*9b8f3200SMarcus Folkesson 						&shadow_plane_state->fmtcnv_state);
485*9b8f3200SMarcus Folkesson 
486*9b8f3200SMarcus Folkesson 		st7571->pformat->update_rect(fb, &damage);
487*9b8f3200SMarcus Folkesson 	}
488*9b8f3200SMarcus Folkesson 
489*9b8f3200SMarcus Folkesson 	drm_dev_exit(idx);
490*9b8f3200SMarcus Folkesson 
491*9b8f3200SMarcus Folkesson out_drm_gem_fb_end_cpu_access:
492*9b8f3200SMarcus Folkesson 	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
493*9b8f3200SMarcus Folkesson }
494*9b8f3200SMarcus Folkesson 
495*9b8f3200SMarcus Folkesson static void st7571_primary_plane_helper_atomic_disable(struct drm_plane *plane,
496*9b8f3200SMarcus Folkesson 						       struct drm_atomic_state *state)
497*9b8f3200SMarcus Folkesson {
498*9b8f3200SMarcus Folkesson 	struct drm_device *dev = plane->dev;
499*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = drm_to_st7571(plane->dev);
500*9b8f3200SMarcus Folkesson 	int idx;
501*9b8f3200SMarcus Folkesson 
502*9b8f3200SMarcus Folkesson 	if (!drm_dev_enter(dev, &idx))
503*9b8f3200SMarcus Folkesson 		return;
504*9b8f3200SMarcus Folkesson 
505*9b8f3200SMarcus Folkesson 	st7571_fb_clear_screen(st7571);
506*9b8f3200SMarcus Folkesson 	drm_dev_exit(idx);
507*9b8f3200SMarcus Folkesson }
508*9b8f3200SMarcus Folkesson 
509*9b8f3200SMarcus Folkesson static const struct drm_plane_helper_funcs st7571_primary_plane_helper_funcs = {
510*9b8f3200SMarcus Folkesson 	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
511*9b8f3200SMarcus Folkesson 	.atomic_check = st7571_primary_plane_helper_atomic_check,
512*9b8f3200SMarcus Folkesson 	.atomic_update = st7571_primary_plane_helper_atomic_update,
513*9b8f3200SMarcus Folkesson 	.atomic_disable = st7571_primary_plane_helper_atomic_disable,
514*9b8f3200SMarcus Folkesson };
515*9b8f3200SMarcus Folkesson 
516*9b8f3200SMarcus Folkesson static const struct drm_plane_funcs st7571_primary_plane_funcs = {
517*9b8f3200SMarcus Folkesson 	.update_plane = drm_atomic_helper_update_plane,
518*9b8f3200SMarcus Folkesson 	.disable_plane = drm_atomic_helper_disable_plane,
519*9b8f3200SMarcus Folkesson 	.destroy = drm_plane_cleanup,
520*9b8f3200SMarcus Folkesson 	DRM_GEM_SHADOW_PLANE_FUNCS,
521*9b8f3200SMarcus Folkesson };
522*9b8f3200SMarcus Folkesson 
523*9b8f3200SMarcus Folkesson /*
524*9b8f3200SMarcus Folkesson  * CRTC
525*9b8f3200SMarcus Folkesson  */
526*9b8f3200SMarcus Folkesson 
527*9b8f3200SMarcus Folkesson static enum drm_mode_status st7571_crtc_mode_valid(struct drm_crtc *crtc,
528*9b8f3200SMarcus Folkesson 						   const struct drm_display_mode *mode)
529*9b8f3200SMarcus Folkesson {
530*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = drm_to_st7571(crtc->dev);
531*9b8f3200SMarcus Folkesson 
532*9b8f3200SMarcus Folkesson 	return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7571->mode);
533*9b8f3200SMarcus Folkesson }
534*9b8f3200SMarcus Folkesson 
535*9b8f3200SMarcus Folkesson static const struct drm_crtc_helper_funcs st7571_crtc_helper_funcs = {
536*9b8f3200SMarcus Folkesson 	.atomic_check = drm_crtc_helper_atomic_check,
537*9b8f3200SMarcus Folkesson 	.mode_valid = st7571_crtc_mode_valid,
538*9b8f3200SMarcus Folkesson };
539*9b8f3200SMarcus Folkesson 
540*9b8f3200SMarcus Folkesson static const struct drm_crtc_funcs st7571_crtc_funcs = {
541*9b8f3200SMarcus Folkesson 	.reset = drm_atomic_helper_crtc_reset,
542*9b8f3200SMarcus Folkesson 	.destroy = drm_crtc_cleanup,
543*9b8f3200SMarcus Folkesson 	.set_config = drm_atomic_helper_set_config,
544*9b8f3200SMarcus Folkesson 	.page_flip = drm_atomic_helper_page_flip,
545*9b8f3200SMarcus Folkesson 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
546*9b8f3200SMarcus Folkesson 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
547*9b8f3200SMarcus Folkesson };
548*9b8f3200SMarcus Folkesson 
549*9b8f3200SMarcus Folkesson /*
550*9b8f3200SMarcus Folkesson  * Encoder
551*9b8f3200SMarcus Folkesson  */
552*9b8f3200SMarcus Folkesson 
553*9b8f3200SMarcus Folkesson static void ssd130x_encoder_atomic_enable(struct drm_encoder *encoder,
554*9b8f3200SMarcus Folkesson 					  struct drm_atomic_state *state)
555*9b8f3200SMarcus Folkesson {
556*9b8f3200SMarcus Folkesson 	struct drm_device *drm = encoder->dev;
557*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = drm_to_st7571(drm);
558*9b8f3200SMarcus Folkesson 	u8 command = ST7571_DISPLAY_ON;
559*9b8f3200SMarcus Folkesson 	int ret;
560*9b8f3200SMarcus Folkesson 
561*9b8f3200SMarcus Folkesson 	ret = st7571->pdata->init(st7571);
562*9b8f3200SMarcus Folkesson 	if (ret)
563*9b8f3200SMarcus Folkesson 		return;
564*9b8f3200SMarcus Folkesson 
565*9b8f3200SMarcus Folkesson 	st7571_send_command_list(st7571, &command, 1);
566*9b8f3200SMarcus Folkesson }
567*9b8f3200SMarcus Folkesson 
568*9b8f3200SMarcus Folkesson static void ssd130x_encoder_atomic_disable(struct drm_encoder *encoder,
569*9b8f3200SMarcus Folkesson 					   struct drm_atomic_state *state)
570*9b8f3200SMarcus Folkesson {
571*9b8f3200SMarcus Folkesson 	struct drm_device *drm = encoder->dev;
572*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = drm_to_st7571(drm);
573*9b8f3200SMarcus Folkesson 	u8 command = ST7571_DISPLAY_OFF;
574*9b8f3200SMarcus Folkesson 
575*9b8f3200SMarcus Folkesson 	st7571_send_command_list(st7571, &command, 1);
576*9b8f3200SMarcus Folkesson }
577*9b8f3200SMarcus Folkesson 
578*9b8f3200SMarcus Folkesson static const struct drm_encoder_funcs st7571_encoder_funcs = {
579*9b8f3200SMarcus Folkesson 	.destroy = drm_encoder_cleanup,
580*9b8f3200SMarcus Folkesson 
581*9b8f3200SMarcus Folkesson };
582*9b8f3200SMarcus Folkesson 
583*9b8f3200SMarcus Folkesson static const struct drm_encoder_helper_funcs st7571_encoder_helper_funcs = {
584*9b8f3200SMarcus Folkesson 	.atomic_enable = ssd130x_encoder_atomic_enable,
585*9b8f3200SMarcus Folkesson 	.atomic_disable = ssd130x_encoder_atomic_disable,
586*9b8f3200SMarcus Folkesson };
587*9b8f3200SMarcus Folkesson 
588*9b8f3200SMarcus Folkesson /*
589*9b8f3200SMarcus Folkesson  * Connector
590*9b8f3200SMarcus Folkesson  */
591*9b8f3200SMarcus Folkesson 
592*9b8f3200SMarcus Folkesson static const struct drm_connector_funcs st7571_connector_funcs = {
593*9b8f3200SMarcus Folkesson 	.reset = drm_atomic_helper_connector_reset,
594*9b8f3200SMarcus Folkesson 	.fill_modes = drm_helper_probe_single_connector_modes,
595*9b8f3200SMarcus Folkesson 	.destroy = drm_connector_cleanup,
596*9b8f3200SMarcus Folkesson 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
597*9b8f3200SMarcus Folkesson 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
598*9b8f3200SMarcus Folkesson };
599*9b8f3200SMarcus Folkesson 
600*9b8f3200SMarcus Folkesson static const struct drm_mode_config_funcs st7571_mode_config_funcs = {
601*9b8f3200SMarcus Folkesson 	.fb_create = drm_gem_fb_create_with_dirty,
602*9b8f3200SMarcus Folkesson 	.atomic_check = drm_atomic_helper_check,
603*9b8f3200SMarcus Folkesson 	.atomic_commit = drm_atomic_helper_commit,
604*9b8f3200SMarcus Folkesson };
605*9b8f3200SMarcus Folkesson 
606*9b8f3200SMarcus Folkesson static struct drm_display_mode st7571_mode(struct st7571_device *st7571)
607*9b8f3200SMarcus Folkesson {
608*9b8f3200SMarcus Folkesson 	struct drm_display_mode mode = {
609*9b8f3200SMarcus Folkesson 		DRM_SIMPLE_MODE(st7571->ncols, st7571->nlines,
610*9b8f3200SMarcus Folkesson 				st7571->width_mm, st7571->height_mm),
611*9b8f3200SMarcus Folkesson 	};
612*9b8f3200SMarcus Folkesson 
613*9b8f3200SMarcus Folkesson 	return mode;
614*9b8f3200SMarcus Folkesson }
615*9b8f3200SMarcus Folkesson 
616*9b8f3200SMarcus Folkesson static int st7571_mode_config_init(struct st7571_device *st7571)
617*9b8f3200SMarcus Folkesson {
618*9b8f3200SMarcus Folkesson 	struct drm_device *dev = &st7571->dev;
619*9b8f3200SMarcus Folkesson 	const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
620*9b8f3200SMarcus Folkesson 	int ret;
621*9b8f3200SMarcus Folkesson 
622*9b8f3200SMarcus Folkesson 	ret = drmm_mode_config_init(dev);
623*9b8f3200SMarcus Folkesson 	if (ret)
624*9b8f3200SMarcus Folkesson 		return ret;
625*9b8f3200SMarcus Folkesson 
626*9b8f3200SMarcus Folkesson 	dev->mode_config.min_width = constraints->min_ncols;
627*9b8f3200SMarcus Folkesson 	dev->mode_config.min_height = constraints->min_nlines;
628*9b8f3200SMarcus Folkesson 	dev->mode_config.max_width = constraints->max_ncols;
629*9b8f3200SMarcus Folkesson 	dev->mode_config.max_height = constraints->max_nlines;
630*9b8f3200SMarcus Folkesson 	dev->mode_config.preferred_depth = 24;
631*9b8f3200SMarcus Folkesson 	dev->mode_config.funcs = &st7571_mode_config_funcs;
632*9b8f3200SMarcus Folkesson 
633*9b8f3200SMarcus Folkesson 	return 0;
634*9b8f3200SMarcus Folkesson }
635*9b8f3200SMarcus Folkesson 
636*9b8f3200SMarcus Folkesson static int st7571_plane_init(struct st7571_device *st7571,
637*9b8f3200SMarcus Folkesson 			     const struct st7571_panel_format *pformat)
638*9b8f3200SMarcus Folkesson {
639*9b8f3200SMarcus Folkesson 	struct drm_plane *primary_plane = &st7571->primary_plane;
640*9b8f3200SMarcus Folkesson 	struct drm_device *dev = &st7571->dev;
641*9b8f3200SMarcus Folkesson 	int ret;
642*9b8f3200SMarcus Folkesson 
643*9b8f3200SMarcus Folkesson 	ret = drm_universal_plane_init(dev, primary_plane, 0,
644*9b8f3200SMarcus Folkesson 				       &st7571_primary_plane_funcs,
645*9b8f3200SMarcus Folkesson 				       pformat->formats,
646*9b8f3200SMarcus Folkesson 				       pformat->nformats,
647*9b8f3200SMarcus Folkesson 				       st7571_primary_plane_fmtmods,
648*9b8f3200SMarcus Folkesson 				       DRM_PLANE_TYPE_PRIMARY, NULL);
649*9b8f3200SMarcus Folkesson 	if (ret)
650*9b8f3200SMarcus Folkesson 		return ret;
651*9b8f3200SMarcus Folkesson 
652*9b8f3200SMarcus Folkesson 	drm_plane_helper_add(primary_plane, &st7571_primary_plane_helper_funcs);
653*9b8f3200SMarcus Folkesson 	drm_plane_enable_fb_damage_clips(primary_plane);
654*9b8f3200SMarcus Folkesson 
655*9b8f3200SMarcus Folkesson 	return 0;
656*9b8f3200SMarcus Folkesson }
657*9b8f3200SMarcus Folkesson 
658*9b8f3200SMarcus Folkesson static int st7571_crtc_init(struct st7571_device *st7571)
659*9b8f3200SMarcus Folkesson {
660*9b8f3200SMarcus Folkesson 	struct drm_plane *primary_plane = &st7571->primary_plane;
661*9b8f3200SMarcus Folkesson 	struct drm_crtc *crtc = &st7571->crtc;
662*9b8f3200SMarcus Folkesson 	struct drm_device *dev = &st7571->dev;
663*9b8f3200SMarcus Folkesson 	int ret;
664*9b8f3200SMarcus Folkesson 
665*9b8f3200SMarcus Folkesson 	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
666*9b8f3200SMarcus Folkesson 					&st7571_crtc_funcs, NULL);
667*9b8f3200SMarcus Folkesson 	if (ret)
668*9b8f3200SMarcus Folkesson 		return ret;
669*9b8f3200SMarcus Folkesson 
670*9b8f3200SMarcus Folkesson 	drm_crtc_helper_add(crtc, &st7571_crtc_helper_funcs);
671*9b8f3200SMarcus Folkesson 
672*9b8f3200SMarcus Folkesson 	return 0;
673*9b8f3200SMarcus Folkesson }
674*9b8f3200SMarcus Folkesson 
675*9b8f3200SMarcus Folkesson static int st7571_encoder_init(struct st7571_device *st7571)
676*9b8f3200SMarcus Folkesson {
677*9b8f3200SMarcus Folkesson 	struct drm_encoder *encoder = &st7571->encoder;
678*9b8f3200SMarcus Folkesson 	struct drm_crtc *crtc = &st7571->crtc;
679*9b8f3200SMarcus Folkesson 	struct drm_device *dev = &st7571->dev;
680*9b8f3200SMarcus Folkesson 	int ret;
681*9b8f3200SMarcus Folkesson 
682*9b8f3200SMarcus Folkesson 	ret = drm_encoder_init(dev, encoder, &st7571_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
683*9b8f3200SMarcus Folkesson 	if (ret)
684*9b8f3200SMarcus Folkesson 		return ret;
685*9b8f3200SMarcus Folkesson 
686*9b8f3200SMarcus Folkesson 	drm_encoder_helper_add(encoder, &st7571_encoder_helper_funcs);
687*9b8f3200SMarcus Folkesson 
688*9b8f3200SMarcus Folkesson 	encoder->possible_crtcs = drm_crtc_mask(crtc);
689*9b8f3200SMarcus Folkesson 
690*9b8f3200SMarcus Folkesson 	return 0;
691*9b8f3200SMarcus Folkesson }
692*9b8f3200SMarcus Folkesson 
693*9b8f3200SMarcus Folkesson static int st7571_connector_init(struct st7571_device *st7571)
694*9b8f3200SMarcus Folkesson {
695*9b8f3200SMarcus Folkesson 	struct drm_connector *connector = &st7571->connector;
696*9b8f3200SMarcus Folkesson 	struct drm_encoder *encoder = &st7571->encoder;
697*9b8f3200SMarcus Folkesson 	struct drm_device *dev = &st7571->dev;
698*9b8f3200SMarcus Folkesson 	int ret;
699*9b8f3200SMarcus Folkesson 
700*9b8f3200SMarcus Folkesson 	ret = drm_connector_init(dev, connector, &st7571_connector_funcs,
701*9b8f3200SMarcus Folkesson 				 DRM_MODE_CONNECTOR_Unknown);
702*9b8f3200SMarcus Folkesson 	if (ret)
703*9b8f3200SMarcus Folkesson 		return ret;
704*9b8f3200SMarcus Folkesson 
705*9b8f3200SMarcus Folkesson 	drm_connector_helper_add(connector, &st7571_connector_helper_funcs);
706*9b8f3200SMarcus Folkesson 
707*9b8f3200SMarcus Folkesson 	return drm_connector_attach_encoder(connector, encoder);
708*9b8f3200SMarcus Folkesson }
709*9b8f3200SMarcus Folkesson 
710*9b8f3200SMarcus Folkesson DEFINE_DRM_GEM_FOPS(st7571_fops);
711*9b8f3200SMarcus Folkesson 
712*9b8f3200SMarcus Folkesson static const struct drm_driver st7571_driver = {
713*9b8f3200SMarcus Folkesson 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
714*9b8f3200SMarcus Folkesson 
715*9b8f3200SMarcus Folkesson 	.name		 = DRIVER_NAME,
716*9b8f3200SMarcus Folkesson 	.desc		 = DRIVER_DESC,
717*9b8f3200SMarcus Folkesson 	.major		 = DRIVER_MAJOR,
718*9b8f3200SMarcus Folkesson 	.minor		 = DRIVER_MINOR,
719*9b8f3200SMarcus Folkesson 
720*9b8f3200SMarcus Folkesson 	.fops		 = &st7571_fops,
721*9b8f3200SMarcus Folkesson 	DRM_GEM_SHMEM_DRIVER_OPS,
722*9b8f3200SMarcus Folkesson 	DRM_FBDEV_SHMEM_DRIVER_OPS,
723*9b8f3200SMarcus Folkesson };
724*9b8f3200SMarcus Folkesson 
725*9b8f3200SMarcus Folkesson static const struct regmap_bus st7571_regmap_bus = {
726*9b8f3200SMarcus Folkesson 	.read = st7571_regmap_read,
727*9b8f3200SMarcus Folkesson 	.write = st7571_regmap_write,
728*9b8f3200SMarcus Folkesson };
729*9b8f3200SMarcus Folkesson 
730*9b8f3200SMarcus Folkesson static const struct regmap_config st7571_regmap_config = {
731*9b8f3200SMarcus Folkesson 	.reg_bits = 8,
732*9b8f3200SMarcus Folkesson 	.val_bits = 8,
733*9b8f3200SMarcus Folkesson 	.use_single_write = true,
734*9b8f3200SMarcus Folkesson };
735*9b8f3200SMarcus Folkesson 
736*9b8f3200SMarcus Folkesson static int st7571_validate_parameters(struct st7571_device *st7571)
737*9b8f3200SMarcus Folkesson {
738*9b8f3200SMarcus Folkesson 	struct device *dev = st7571->dev.dev;
739*9b8f3200SMarcus Folkesson 	const struct st7571_panel_constraints *constraints = &st7571->pdata->constraints;
740*9b8f3200SMarcus Folkesson 
741*9b8f3200SMarcus Folkesson 	if (st7571->width_mm  == 0) {
742*9b8f3200SMarcus Folkesson 		dev_err(dev, "Invalid panel width\n");
743*9b8f3200SMarcus Folkesson 		return -EINVAL;
744*9b8f3200SMarcus Folkesson 	}
745*9b8f3200SMarcus Folkesson 
746*9b8f3200SMarcus Folkesson 	if (st7571->height_mm == 0) {
747*9b8f3200SMarcus Folkesson 		dev_err(dev, "Invalid panel height\n");
748*9b8f3200SMarcus Folkesson 		return -EINVAL;
749*9b8f3200SMarcus Folkesson 	}
750*9b8f3200SMarcus Folkesson 
751*9b8f3200SMarcus Folkesson 	if (st7571->nlines < constraints->min_nlines ||
752*9b8f3200SMarcus Folkesson 	    st7571->nlines > constraints->max_nlines) {
753*9b8f3200SMarcus Folkesson 		dev_err(dev, "Invalid timing configuration.\n");
754*9b8f3200SMarcus Folkesson 		return -EINVAL;
755*9b8f3200SMarcus Folkesson 	}
756*9b8f3200SMarcus Folkesson 
757*9b8f3200SMarcus Folkesson 	if (st7571->startline + st7571->nlines > constraints->max_nlines) {
758*9b8f3200SMarcus Folkesson 		dev_err(dev, "Invalid timing configuration.\n");
759*9b8f3200SMarcus Folkesson 		return -EINVAL;
760*9b8f3200SMarcus Folkesson 	}
761*9b8f3200SMarcus Folkesson 
762*9b8f3200SMarcus Folkesson 	if (st7571->ncols < constraints->min_ncols ||
763*9b8f3200SMarcus Folkesson 	    st7571->ncols > constraints->max_ncols) {
764*9b8f3200SMarcus Folkesson 		dev_err(dev, "Invalid timing configuration.\n");
765*9b8f3200SMarcus Folkesson 		return -EINVAL;
766*9b8f3200SMarcus Folkesson 	}
767*9b8f3200SMarcus Folkesson 
768*9b8f3200SMarcus Folkesson 	if (st7571->grayscale && !constraints->support_grayscale) {
769*9b8f3200SMarcus Folkesson 		dev_err(dev, "Grayscale not supported\n");
770*9b8f3200SMarcus Folkesson 		return -EINVAL;
771*9b8f3200SMarcus Folkesson 	}
772*9b8f3200SMarcus Folkesson 
773*9b8f3200SMarcus Folkesson 	return 0;
774*9b8f3200SMarcus Folkesson }
775*9b8f3200SMarcus Folkesson 
776*9b8f3200SMarcus Folkesson static int st7571_parse_dt(struct st7571_device *st7571)
777*9b8f3200SMarcus Folkesson {
778*9b8f3200SMarcus Folkesson 	struct device *dev = &st7571->client->dev;
779*9b8f3200SMarcus Folkesson 	struct device_node *np = dev->of_node;
780*9b8f3200SMarcus Folkesson 	struct display_timing dt;
781*9b8f3200SMarcus Folkesson 	int ret;
782*9b8f3200SMarcus Folkesson 
783*9b8f3200SMarcus Folkesson 	ret = of_get_display_timing(np, "panel-timing", &dt);
784*9b8f3200SMarcus Folkesson 	if (ret) {
785*9b8f3200SMarcus Folkesson 		dev_err(dev, "Failed to get display timing from DT\n");
786*9b8f3200SMarcus Folkesson 		return ret;
787*9b8f3200SMarcus Folkesson 	}
788*9b8f3200SMarcus Folkesson 
789*9b8f3200SMarcus Folkesson 	of_property_read_u32(np, "width-mm", &st7571->width_mm);
790*9b8f3200SMarcus Folkesson 	of_property_read_u32(np, "height-mm", &st7571->height_mm);
791*9b8f3200SMarcus Folkesson 	st7571->grayscale = of_property_read_bool(np, "sitronix,grayscale");
792*9b8f3200SMarcus Folkesson 
793*9b8f3200SMarcus Folkesson 	if (st7571->grayscale) {
794*9b8f3200SMarcus Folkesson 		st7571->pformat = &st7571_grayscale;
795*9b8f3200SMarcus Folkesson 		st7571->bpp = 2;
796*9b8f3200SMarcus Folkesson 	} else {
797*9b8f3200SMarcus Folkesson 		st7571->pformat = &st7571_monochrome;
798*9b8f3200SMarcus Folkesson 		st7571->bpp = 1;
799*9b8f3200SMarcus Folkesson 	}
800*9b8f3200SMarcus Folkesson 
801*9b8f3200SMarcus Folkesson 	st7571->startline = dt.vfront_porch.typ;
802*9b8f3200SMarcus Folkesson 	st7571->nlines = dt.vactive.typ;
803*9b8f3200SMarcus Folkesson 	st7571->ncols = dt.hactive.typ;
804*9b8f3200SMarcus Folkesson 
805*9b8f3200SMarcus Folkesson 	st7571->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
806*9b8f3200SMarcus Folkesson 	if (IS_ERR(st7571->reset))
807*9b8f3200SMarcus Folkesson 		return PTR_ERR(st7571->reset);
808*9b8f3200SMarcus Folkesson 
809*9b8f3200SMarcus Folkesson 	return 0;
810*9b8f3200SMarcus Folkesson }
811*9b8f3200SMarcus Folkesson 
812*9b8f3200SMarcus Folkesson static void st7571_reset(struct st7571_device *st7571)
813*9b8f3200SMarcus Folkesson {
814*9b8f3200SMarcus Folkesson 	gpiod_set_value_cansleep(st7571->reset, 1);
815*9b8f3200SMarcus Folkesson 	fsleep(20);
816*9b8f3200SMarcus Folkesson 	gpiod_set_value_cansleep(st7571->reset, 0);
817*9b8f3200SMarcus Folkesson }
818*9b8f3200SMarcus Folkesson 
819*9b8f3200SMarcus Folkesson static int st7571_lcd_init(struct st7571_device *st7571)
820*9b8f3200SMarcus Folkesson {
821*9b8f3200SMarcus Folkesson 	/*
822*9b8f3200SMarcus Folkesson 	 * Most of the initialization sequence is taken directly from the
823*9b8f3200SMarcus Folkesson 	 * referential initial code in the ST7571 datasheet.
824*9b8f3200SMarcus Folkesson 	 */
825*9b8f3200SMarcus Folkesson 	u8 commands[] = {
826*9b8f3200SMarcus Folkesson 		ST7571_DISPLAY_OFF,
827*9b8f3200SMarcus Folkesson 
828*9b8f3200SMarcus Folkesson 		ST7571_SET_MODE_MSB,
829*9b8f3200SMarcus Folkesson 		ST7571_SET_MODE_LSB(0x2e),
830*9b8f3200SMarcus Folkesson 
831*9b8f3200SMarcus Folkesson 		ST7571_SET_SEG_SCAN_DIR(0),
832*9b8f3200SMarcus Folkesson 		ST7571_SET_COM_SCAN_DIR(1),
833*9b8f3200SMarcus Folkesson 
834*9b8f3200SMarcus Folkesson 		ST7571_SET_COM0_MSB,
835*9b8f3200SMarcus Folkesson 		ST7571_SET_COM0_LSB(0x00),
836*9b8f3200SMarcus Folkesson 
837*9b8f3200SMarcus Folkesson 		ST7571_SET_START_LINE_MSB,
838*9b8f3200SMarcus Folkesson 		ST7571_SET_START_LINE_LSB(st7571->startline),
839*9b8f3200SMarcus Folkesson 
840*9b8f3200SMarcus Folkesson 		ST7571_OSC_ON,
841*9b8f3200SMarcus Folkesson 		ST7571_SET_REGULATOR_REG(5),
842*9b8f3200SMarcus Folkesson 		ST7571_SET_CONTRAST_MSB,
843*9b8f3200SMarcus Folkesson 		ST7571_SET_CONTRAST_LSB(0x33),
844*9b8f3200SMarcus Folkesson 		ST7571_SET_LCD_BIAS(0x04),
845*9b8f3200SMarcus Folkesson 		ST7571_SET_DISPLAY_DUTY_MSB,
846*9b8f3200SMarcus Folkesson 		ST7571_SET_DISPLAY_DUTY_LSB(st7571->nlines),
847*9b8f3200SMarcus Folkesson 
848*9b8f3200SMarcus Folkesson 		ST7571_SET_POWER(0x4),	/* Power Control, VC: ON, VR: OFF, VF: OFF */
849*9b8f3200SMarcus Folkesson 		ST7571_SET_POWER(0x6),	/* Power Control, VC: ON, VR: ON, VF: OFF */
850*9b8f3200SMarcus Folkesson 		ST7571_SET_POWER(0x7),	/* Power Control, VC: ON, VR: ON, VF: ON */
851*9b8f3200SMarcus Folkesson 
852*9b8f3200SMarcus Folkesson 		ST7571_COMMAND_SET_3,
853*9b8f3200SMarcus Folkesson 		ST7571_SET_COLOR_MODE(st7571->pformat->mode),
854*9b8f3200SMarcus Folkesson 		ST7571_COMMAND_SET_NORMAL,
855*9b8f3200SMarcus Folkesson 
856*9b8f3200SMarcus Folkesson 		ST7571_SET_REVERSE(0),
857*9b8f3200SMarcus Folkesson 		ST7571_SET_ENTIRE_DISPLAY_ON(0),
858*9b8f3200SMarcus Folkesson 	};
859*9b8f3200SMarcus Folkesson 
860*9b8f3200SMarcus Folkesson 	/* Perform a reset before initializing the controller */
861*9b8f3200SMarcus Folkesson 	st7571_reset(st7571);
862*9b8f3200SMarcus Folkesson 
863*9b8f3200SMarcus Folkesson 	return st7571_send_command_list(st7571, commands, ARRAY_SIZE(commands));
864*9b8f3200SMarcus Folkesson }
865*9b8f3200SMarcus Folkesson 
866*9b8f3200SMarcus Folkesson static int st7571_probe(struct i2c_client *client)
867*9b8f3200SMarcus Folkesson {
868*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571;
869*9b8f3200SMarcus Folkesson 	struct drm_device *dev;
870*9b8f3200SMarcus Folkesson 	int ret;
871*9b8f3200SMarcus Folkesson 
872*9b8f3200SMarcus Folkesson 	st7571 = devm_drm_dev_alloc(&client->dev, &st7571_driver,
873*9b8f3200SMarcus Folkesson 				    struct st7571_device, dev);
874*9b8f3200SMarcus Folkesson 	if (IS_ERR(st7571))
875*9b8f3200SMarcus Folkesson 		return PTR_ERR(st7571);
876*9b8f3200SMarcus Folkesson 
877*9b8f3200SMarcus Folkesson 	dev = &st7571->dev;
878*9b8f3200SMarcus Folkesson 	st7571->client = client;
879*9b8f3200SMarcus Folkesson 	i2c_set_clientdata(client, st7571);
880*9b8f3200SMarcus Folkesson 	st7571->pdata = device_get_match_data(&client->dev);
881*9b8f3200SMarcus Folkesson 
882*9b8f3200SMarcus Folkesson 	ret = st7571_parse_dt(st7571);
883*9b8f3200SMarcus Folkesson 	if (ret)
884*9b8f3200SMarcus Folkesson 		return ret;
885*9b8f3200SMarcus Folkesson 
886*9b8f3200SMarcus Folkesson 	ret = st7571_validate_parameters(st7571);
887*9b8f3200SMarcus Folkesson 	if (ret)
888*9b8f3200SMarcus Folkesson 		return ret;
889*9b8f3200SMarcus Folkesson 
890*9b8f3200SMarcus Folkesson 	st7571->mode = st7571_mode(st7571);
891*9b8f3200SMarcus Folkesson 
892*9b8f3200SMarcus Folkesson 	/*
893*9b8f3200SMarcus Folkesson 	 * The hardware design could make it hard to detect a NAK on the I2C bus.
894*9b8f3200SMarcus Folkesson 	 * If the adapter does not support protocol mangling do
895*9b8f3200SMarcus Folkesson 	 * not set the I2C_M_IGNORE_NAK flag at the expense * of possible
896*9b8f3200SMarcus Folkesson 	 * cruft in the logs.
897*9b8f3200SMarcus Folkesson 	 */
898*9b8f3200SMarcus Folkesson 	if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING))
899*9b8f3200SMarcus Folkesson 		st7571->ignore_nak = true;
900*9b8f3200SMarcus Folkesson 
901*9b8f3200SMarcus Folkesson 	st7571->regmap = devm_regmap_init(&client->dev, &st7571_regmap_bus,
902*9b8f3200SMarcus Folkesson 					  client, &st7571_regmap_config);
903*9b8f3200SMarcus Folkesson 	if (IS_ERR(st7571->regmap)) {
904*9b8f3200SMarcus Folkesson 		return dev_err_probe(&client->dev, PTR_ERR(st7571->regmap),
905*9b8f3200SMarcus Folkesson 				     "Failed to initialize regmap\n");
906*9b8f3200SMarcus Folkesson 	}
907*9b8f3200SMarcus Folkesson 
908*9b8f3200SMarcus Folkesson 	st7571->hwbuf = devm_kzalloc(&client->dev,
909*9b8f3200SMarcus Folkesson 				     (st7571->nlines * st7571->ncols * st7571->bpp) / 8,
910*9b8f3200SMarcus Folkesson 				     GFP_KERNEL);
911*9b8f3200SMarcus Folkesson 	if (!st7571->hwbuf)
912*9b8f3200SMarcus Folkesson 		return -ENOMEM;
913*9b8f3200SMarcus Folkesson 
914*9b8f3200SMarcus Folkesson 	st7571->row = devm_kzalloc(&client->dev,
915*9b8f3200SMarcus Folkesson 				   (st7571->ncols * st7571->bpp),
916*9b8f3200SMarcus Folkesson 				   GFP_KERNEL);
917*9b8f3200SMarcus Folkesson 	if (!st7571->row)
918*9b8f3200SMarcus Folkesson 		return -ENOMEM;
919*9b8f3200SMarcus Folkesson 
920*9b8f3200SMarcus Folkesson 	ret = st7571_mode_config_init(st7571);
921*9b8f3200SMarcus Folkesson 	if (ret)
922*9b8f3200SMarcus Folkesson 		return dev_err_probe(&client->dev, ret,
923*9b8f3200SMarcus Folkesson 				     "Failed to initialize mode config\n");
924*9b8f3200SMarcus Folkesson 
925*9b8f3200SMarcus Folkesson 	ret = st7571_plane_init(st7571, st7571->pformat);
926*9b8f3200SMarcus Folkesson 	if (ret)
927*9b8f3200SMarcus Folkesson 		return dev_err_probe(&client->dev, ret,
928*9b8f3200SMarcus Folkesson 				     "Failed to initialize primary plane\n");
929*9b8f3200SMarcus Folkesson 
930*9b8f3200SMarcus Folkesson 	ret = st7571_crtc_init(st7571);
931*9b8f3200SMarcus Folkesson 	if (ret < 0)
932*9b8f3200SMarcus Folkesson 		return dev_err_probe(&client->dev, ret,
933*9b8f3200SMarcus Folkesson 				     "Failed to initialize CRTC\n");
934*9b8f3200SMarcus Folkesson 
935*9b8f3200SMarcus Folkesson 	ret = st7571_encoder_init(st7571);
936*9b8f3200SMarcus Folkesson 	if (ret < 0)
937*9b8f3200SMarcus Folkesson 		return dev_err_probe(&client->dev, ret,
938*9b8f3200SMarcus Folkesson 				     "Failed to initialize encoder\n");
939*9b8f3200SMarcus Folkesson 
940*9b8f3200SMarcus Folkesson 	ret = st7571_connector_init(st7571);
941*9b8f3200SMarcus Folkesson 	if (ret < 0)
942*9b8f3200SMarcus Folkesson 		return dev_err_probe(&client->dev, ret,
943*9b8f3200SMarcus Folkesson 				     "Failed to initialize connector\n");
944*9b8f3200SMarcus Folkesson 
945*9b8f3200SMarcus Folkesson 	drm_mode_config_reset(dev);
946*9b8f3200SMarcus Folkesson 
947*9b8f3200SMarcus Folkesson 	ret = drm_dev_register(dev, 0);
948*9b8f3200SMarcus Folkesson 	if (ret)
949*9b8f3200SMarcus Folkesson 		return dev_err_probe(&client->dev, ret,
950*9b8f3200SMarcus Folkesson 				     "Failed to register DRM device\n");
951*9b8f3200SMarcus Folkesson 
952*9b8f3200SMarcus Folkesson 	drm_client_setup(dev, NULL);
953*9b8f3200SMarcus Folkesson 	return 0;
954*9b8f3200SMarcus Folkesson }
955*9b8f3200SMarcus Folkesson 
956*9b8f3200SMarcus Folkesson static void st7571_remove(struct i2c_client *client)
957*9b8f3200SMarcus Folkesson {
958*9b8f3200SMarcus Folkesson 	struct st7571_device *st7571 = i2c_get_clientdata(client);
959*9b8f3200SMarcus Folkesson 
960*9b8f3200SMarcus Folkesson 	drm_dev_unplug(&st7571->dev);
961*9b8f3200SMarcus Folkesson }
962*9b8f3200SMarcus Folkesson 
963*9b8f3200SMarcus Folkesson struct st7571_panel_data st7571_config = {
964*9b8f3200SMarcus Folkesson 	.init = st7571_lcd_init,
965*9b8f3200SMarcus Folkesson 	.constraints = {
966*9b8f3200SMarcus Folkesson 		.min_nlines = 1,
967*9b8f3200SMarcus Folkesson 		.max_nlines = 128,
968*9b8f3200SMarcus Folkesson 		.min_ncols = 128,
969*9b8f3200SMarcus Folkesson 		.max_ncols = 128,
970*9b8f3200SMarcus Folkesson 		.support_grayscale = true,
971*9b8f3200SMarcus Folkesson 	},
972*9b8f3200SMarcus Folkesson };
973*9b8f3200SMarcus Folkesson 
974*9b8f3200SMarcus Folkesson static const struct of_device_id st7571_of_match[] = {
975*9b8f3200SMarcus Folkesson 	{ .compatible = "sitronix,st7571", .data = &st7571_config },
976*9b8f3200SMarcus Folkesson 	{},
977*9b8f3200SMarcus Folkesson };
978*9b8f3200SMarcus Folkesson MODULE_DEVICE_TABLE(of, st7571_of_match);
979*9b8f3200SMarcus Folkesson 
980*9b8f3200SMarcus Folkesson static const struct i2c_device_id st7571_id[] = {
981*9b8f3200SMarcus Folkesson 	{ "st7571", 0 },
982*9b8f3200SMarcus Folkesson 	{ }
983*9b8f3200SMarcus Folkesson };
984*9b8f3200SMarcus Folkesson MODULE_DEVICE_TABLE(i2c, st7571_id);
985*9b8f3200SMarcus Folkesson 
986*9b8f3200SMarcus Folkesson static struct i2c_driver st7571_i2c_driver = {
987*9b8f3200SMarcus Folkesson 	.driver = {
988*9b8f3200SMarcus Folkesson 		.name = "st7571",
989*9b8f3200SMarcus Folkesson 		.of_match_table = st7571_of_match,
990*9b8f3200SMarcus Folkesson 	},
991*9b8f3200SMarcus Folkesson 	.probe = st7571_probe,
992*9b8f3200SMarcus Folkesson 	.remove = st7571_remove,
993*9b8f3200SMarcus Folkesson 	.id_table = st7571_id,
994*9b8f3200SMarcus Folkesson };
995*9b8f3200SMarcus Folkesson 
996*9b8f3200SMarcus Folkesson module_i2c_driver(st7571_i2c_driver);
997*9b8f3200SMarcus Folkesson 
998*9b8f3200SMarcus Folkesson MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
999*9b8f3200SMarcus Folkesson MODULE_DESCRIPTION("DRM Driver for Sitronix ST7571 LCD controller");
1000*9b8f3200SMarcus Folkesson MODULE_LICENSE("GPL");
1001