xref: /linux/drivers/gpu/drm/sitronix/st7920.c (revision bf4afc53b77aeaa48b5409da5c8da6bb4eff7f43)
188b09229SIker Pedrosa // SPDX-License-Identifier: GPL-2.0-only
288b09229SIker Pedrosa /*
388b09229SIker Pedrosa  * DRM driver for Sitronix ST7920 LCD displays
488b09229SIker Pedrosa  *
588b09229SIker Pedrosa  * Copyright 2025 Iker Pedrosa <ikerpedrosam@gmail.com>
688b09229SIker Pedrosa  *
788b09229SIker Pedrosa  */
888b09229SIker Pedrosa 
988b09229SIker Pedrosa #include <linux/bitrev.h>
1088b09229SIker Pedrosa #include <linux/delay.h>
1188b09229SIker Pedrosa #include <linux/gpio/consumer.h>
1288b09229SIker Pedrosa #include <linux/module.h>
1388b09229SIker Pedrosa #include <linux/regmap.h>
1488b09229SIker Pedrosa #include <linux/spi/spi.h>
1588b09229SIker Pedrosa 
1688b09229SIker Pedrosa #include <drm/clients/drm_client_setup.h>
1788b09229SIker Pedrosa #include <drm/drm_atomic.h>
1888b09229SIker Pedrosa #include <drm/drm_atomic_helper.h>
1988b09229SIker Pedrosa #include <drm/drm_crtc_helper.h>
2088b09229SIker Pedrosa #include <drm/drm_damage_helper.h>
2188b09229SIker Pedrosa #include <drm/drm_drv.h>
2288b09229SIker Pedrosa #include <drm/drm_fbdev_shmem.h>
2388b09229SIker Pedrosa #include <drm/drm_framebuffer.h>
2488b09229SIker Pedrosa #include <drm/drm_gem_atomic_helper.h>
2588b09229SIker Pedrosa #include <drm/drm_gem_framebuffer_helper.h>
2688b09229SIker Pedrosa #include <drm/drm_gem_shmem_helper.h>
2788b09229SIker Pedrosa #include <drm/drm_plane.h>
2888b09229SIker Pedrosa #include <drm/drm_print.h>
2988b09229SIker Pedrosa #include <drm/drm_probe_helper.h>
3088b09229SIker Pedrosa 
3188b09229SIker Pedrosa #define DRIVER_NAME	"sitronix_st7920"
3288b09229SIker Pedrosa #define DRIVER_DESC	"DRM driver for Sitronix ST7920 LCD displays"
3388b09229SIker Pedrosa #define DRIVER_MAJOR	1
3488b09229SIker Pedrosa #define DRIVER_MINOR	0
3588b09229SIker Pedrosa 
3688b09229SIker Pedrosa /* Display organization */
3788b09229SIker Pedrosa #define ST7920_PITCH		16
3888b09229SIker Pedrosa #define ST7920_SCANLINES	64
3988b09229SIker Pedrosa #define BYTES_IN_DISPLAY	(ST7920_PITCH * ST7920_SCANLINES)
4088b09229SIker Pedrosa #define BYTES_IN_SEGMENT	2
4188b09229SIker Pedrosa #define PIXELS_PER_SEGMENT	(BYTES_IN_SEGMENT * 8)
4288b09229SIker Pedrosa #define ST7920_DEFAULT_WIDTH	128
4388b09229SIker Pedrosa #define ST7920_DEFAULT_HEIGHT	64
4488b09229SIker Pedrosa 
4588b09229SIker Pedrosa /* Sync sequence */
4688b09229SIker Pedrosa #define SYNC_BITS			0xF8
4788b09229SIker Pedrosa #define RW_HIGH				0x04
4888b09229SIker Pedrosa #define RS_HIGH				0x02
4988b09229SIker Pedrosa 
5088b09229SIker Pedrosa /* Commands */
5188b09229SIker Pedrosa #define SET_DISPLAY_ON			0x0C
5288b09229SIker Pedrosa #define SET_DISPLAY_OFF			0x08
5388b09229SIker Pedrosa #define SET_DISPLAY_CLEAR		0x01
5488b09229SIker Pedrosa #define SET_BASIC_INSTRUCTION_SET	0x30
5588b09229SIker Pedrosa #define SET_EXT_INSTRUCTION_SET		0x34
5688b09229SIker Pedrosa #define SET_GRAPHICS_DISPLAY		0x36
5788b09229SIker Pedrosa #define SET_GDRAM_ADDRESS		0x80
5888b09229SIker Pedrosa #define SET_GDRAM_DATA			0xFF /* Driver internal command */
5988b09229SIker Pedrosa 
6088b09229SIker Pedrosa /* Masks */
6188b09229SIker Pedrosa #define HIGH_DATA_MASK			0xF0
6288b09229SIker Pedrosa #define LOW_DATA_MASK			0x0F
6388b09229SIker Pedrosa #define TOP_VERTICAL_ADDRESS		0x80
6488b09229SIker Pedrosa #define BOTTOM_VERTICAL_ADDRESS		0x60
6588b09229SIker Pedrosa #define TOP_HORIZONTAL_ADDRESS		0x00
6688b09229SIker Pedrosa #define BOTTOM_HORIZONTAL_ADDRESS	0x80
6788b09229SIker Pedrosa 
6888b09229SIker Pedrosa #define CMD_SIZE			35
6988b09229SIker Pedrosa 
7088b09229SIker Pedrosa struct spi7920_error {
7188b09229SIker Pedrosa 	int errno;
7288b09229SIker Pedrosa };
7388b09229SIker Pedrosa 
7488b09229SIker Pedrosa struct st7920_device {
7588b09229SIker Pedrosa 	struct drm_device drm;
7688b09229SIker Pedrosa 	struct drm_display_mode mode;
7788b09229SIker Pedrosa 	struct drm_plane primary_plane;
7888b09229SIker Pedrosa 	struct drm_crtc crtc;
7988b09229SIker Pedrosa 	struct drm_encoder encoder;
8088b09229SIker Pedrosa 	struct drm_connector connector;
8188b09229SIker Pedrosa 	struct spi_device *spi;
8288b09229SIker Pedrosa 
8388b09229SIker Pedrosa 	struct regmap *regmap;
8488b09229SIker Pedrosa 
8588b09229SIker Pedrosa 	struct gpio_desc *reset_gpio;
8688b09229SIker Pedrosa 
8788b09229SIker Pedrosa 	u32 height;
8888b09229SIker Pedrosa 	u32 width;
8988b09229SIker Pedrosa };
9088b09229SIker Pedrosa 
9188b09229SIker Pedrosa struct st7920_plane_state {
9288b09229SIker Pedrosa 	struct drm_shadow_plane_state base;
9388b09229SIker Pedrosa 	/* Intermediate buffer to convert pixels from XRGB8888 to HW format */
9488b09229SIker Pedrosa 	u8 *buffer;
9588b09229SIker Pedrosa };
9688b09229SIker Pedrosa 
9788b09229SIker Pedrosa struct st7920_crtc_state {
9888b09229SIker Pedrosa 	struct drm_crtc_state base;
9988b09229SIker Pedrosa 	/* Buffer to store pixels in HW format and written to the panel */
10088b09229SIker Pedrosa 	u8 *data_array;
10188b09229SIker Pedrosa };
10288b09229SIker Pedrosa 
to_st7920_plane_state(struct drm_plane_state * state)10388b09229SIker Pedrosa static inline struct st7920_plane_state *to_st7920_plane_state(struct drm_plane_state *state)
10488b09229SIker Pedrosa {
10588b09229SIker Pedrosa 	return container_of(state, struct st7920_plane_state, base.base);
10688b09229SIker Pedrosa }
10788b09229SIker Pedrosa 
to_st7920_crtc_state(struct drm_crtc_state * state)10888b09229SIker Pedrosa static inline struct st7920_crtc_state *to_st7920_crtc_state(struct drm_crtc_state *state)
10988b09229SIker Pedrosa {
11088b09229SIker Pedrosa 	return container_of(state, struct st7920_crtc_state, base);
11188b09229SIker Pedrosa }
11288b09229SIker Pedrosa 
drm_to_st7920(struct drm_device * drm)11388b09229SIker Pedrosa static inline struct st7920_device *drm_to_st7920(struct drm_device *drm)
11488b09229SIker Pedrosa {
11588b09229SIker Pedrosa 	return container_of(drm, struct st7920_device, drm);
11688b09229SIker Pedrosa }
11788b09229SIker Pedrosa 
st7920_store_gdram_address(const void * data,u8 * reg)11888b09229SIker Pedrosa static int st7920_store_gdram_address(const void *data, u8 *reg)
11988b09229SIker Pedrosa {
12088b09229SIker Pedrosa 	const u8 y_addr = *(const u8 *)data;
12188b09229SIker Pedrosa 	bool bottom_screen = (y_addr >= 32);
12288b09229SIker Pedrosa 	int i = 0;
12388b09229SIker Pedrosa 
12488b09229SIker Pedrosa 	reg[i++] = SYNC_BITS;
12588b09229SIker Pedrosa 	/* Set vertical address */
12688b09229SIker Pedrosa 	if (!bottom_screen)
12788b09229SIker Pedrosa 		reg[i++] = TOP_VERTICAL_ADDRESS + (*(uint8_t *)data & HIGH_DATA_MASK);
12888b09229SIker Pedrosa 	else
12988b09229SIker Pedrosa 		reg[i++] = BOTTOM_VERTICAL_ADDRESS + (*(uint8_t *)data & HIGH_DATA_MASK);
13088b09229SIker Pedrosa 
13188b09229SIker Pedrosa 	reg[i++] = *(uint8_t *)data << 4;
13288b09229SIker Pedrosa 	/* Set horizontal address */
13388b09229SIker Pedrosa 	reg[i++] = SET_GDRAM_ADDRESS;
13488b09229SIker Pedrosa 	if (!bottom_screen)
13588b09229SIker Pedrosa 		reg[i++] = TOP_HORIZONTAL_ADDRESS;
13688b09229SIker Pedrosa 	else
13788b09229SIker Pedrosa 		reg[i++] = BOTTOM_HORIZONTAL_ADDRESS;
13888b09229SIker Pedrosa 
13988b09229SIker Pedrosa 	return i;
14088b09229SIker Pedrosa }
14188b09229SIker Pedrosa 
st7920_store_gdram_data(const void * data,u8 * reg)14288b09229SIker Pedrosa static int st7920_store_gdram_data(const void *data, u8 *reg)
14388b09229SIker Pedrosa {
14488b09229SIker Pedrosa 	const u8 *line_data = data;
14588b09229SIker Pedrosa 	int i = 0, j = 0;
14688b09229SIker Pedrosa 
14788b09229SIker Pedrosa 	reg[i++] = SYNC_BITS | RS_HIGH;
14888b09229SIker Pedrosa 
14988b09229SIker Pedrosa 	for (j = 0; j < 16; j++) {
15088b09229SIker Pedrosa 		reg[i++] = line_data[j] & 0xF0;
15188b09229SIker Pedrosa 		reg[i++] = (line_data[j] << 4) & 0xF0;
15288b09229SIker Pedrosa 	}
15388b09229SIker Pedrosa 
15488b09229SIker Pedrosa 	return i;
15588b09229SIker Pedrosa }
15688b09229SIker Pedrosa 
st7920_store_others(int cmd,const void * data,u8 * reg)15788b09229SIker Pedrosa static int st7920_store_others(int cmd, const void *data, u8 *reg)
15888b09229SIker Pedrosa {
15988b09229SIker Pedrosa 	int i = 0;
16088b09229SIker Pedrosa 
16188b09229SIker Pedrosa 	reg[i++] = SYNC_BITS;
16288b09229SIker Pedrosa 	reg[i++] = cmd & HIGH_DATA_MASK;
16388b09229SIker Pedrosa 	reg[i++] = (cmd & LOW_DATA_MASK) << 4;
16488b09229SIker Pedrosa 
16588b09229SIker Pedrosa 	return i;
16688b09229SIker Pedrosa }
16788b09229SIker Pedrosa 
st7920_spi_write(struct spi_device * spi,int cmd,const void * data,int delay_us,struct spi7920_error * err)16888b09229SIker Pedrosa static void st7920_spi_write(struct spi_device *spi, int cmd, const void *data,
16988b09229SIker Pedrosa 			     int delay_us, struct spi7920_error *err)
17088b09229SIker Pedrosa {
17188b09229SIker Pedrosa 	u8 reg[CMD_SIZE] = {0};
17288b09229SIker Pedrosa 	int size = 0;
17388b09229SIker Pedrosa 	int ret;
17488b09229SIker Pedrosa 
17588b09229SIker Pedrosa 	if (err->errno)
17688b09229SIker Pedrosa 		return;
17788b09229SIker Pedrosa 
17888b09229SIker Pedrosa 	/*
17988b09229SIker Pedrosa 	 * First the sync bits are sent: 11111WS0.
18088b09229SIker Pedrosa 	 * Where W is the read/write (RW) bit and S is the register/data (RS) bit.
18188b09229SIker Pedrosa 	 * Then, every 8 bits instruction/data will be separated into 2 groups.
18288b09229SIker Pedrosa 	 * Higher 4 bits (DB7~DB4) will be placed in the first section followed by
18388b09229SIker Pedrosa 	 * 4 '0's. And lower 4 bits (DB3~DB0) will be placed in the second section
18488b09229SIker Pedrosa 	 * followed by 4 '0's.
18588b09229SIker Pedrosa 	 */
18688b09229SIker Pedrosa 	if (cmd == SET_GDRAM_ADDRESS)
18788b09229SIker Pedrosa 		size = st7920_store_gdram_address(data, reg);
18888b09229SIker Pedrosa 	else if (cmd == SET_GDRAM_DATA)
18988b09229SIker Pedrosa 		size = st7920_store_gdram_data(data, reg);
19088b09229SIker Pedrosa 	else
19188b09229SIker Pedrosa 		size = st7920_store_others(cmd, data, reg);
19288b09229SIker Pedrosa 
19388b09229SIker Pedrosa 	ret = spi_write(spi, reg, size);
19488b09229SIker Pedrosa 	if (ret) {
19588b09229SIker Pedrosa 		err->errno = ret;
19688b09229SIker Pedrosa 		return;
19788b09229SIker Pedrosa 	}
19888b09229SIker Pedrosa 
19988b09229SIker Pedrosa 	if (delay_us)
20088b09229SIker Pedrosa 		udelay(delay_us);
20188b09229SIker Pedrosa }
20288b09229SIker Pedrosa 
20388b09229SIker Pedrosa static const struct regmap_config st7920_spi_regmap_config = {
20488b09229SIker Pedrosa 	.reg_bits = 8,
20588b09229SIker Pedrosa 	.val_bits = 8,
20688b09229SIker Pedrosa };
20788b09229SIker Pedrosa 
20888b09229SIker Pedrosa static const struct of_device_id st7920_of_match[] = {
20988b09229SIker Pedrosa 	/* st7920 family */
21088b09229SIker Pedrosa 	{
21188b09229SIker Pedrosa 		.compatible = "sitronix,st7920",
21288b09229SIker Pedrosa 	},
21388b09229SIker Pedrosa 	{ /* sentinel */ }
21488b09229SIker Pedrosa };
21588b09229SIker Pedrosa MODULE_DEVICE_TABLE(of, st7920_of_match);
21688b09229SIker Pedrosa 
21788b09229SIker Pedrosa /*
21888b09229SIker Pedrosa  * The SPI core always reports a MODALIAS uevent of the form "spi:<dev>", even
21988b09229SIker Pedrosa  * if the device was registered via OF. This means that the module will not be
22088b09229SIker Pedrosa  * auto loaded, unless it contains an alias that matches the MODALIAS reported.
22188b09229SIker Pedrosa  *
22288b09229SIker Pedrosa  * To workaround this issue, add a SPI device ID table. Even when this should
22388b09229SIker Pedrosa  * not be needed for this driver to match the registered SPI devices.
22488b09229SIker Pedrosa  */
22588b09229SIker Pedrosa static const struct spi_device_id st7920_spi_id[] = {
22688b09229SIker Pedrosa 	/* st7920 family */
22788b09229SIker Pedrosa 	{ "st7920",  0 },
22888b09229SIker Pedrosa 	{ /* sentinel */ }
22988b09229SIker Pedrosa };
23088b09229SIker Pedrosa MODULE_DEVICE_TABLE(spi, st7920_spi_id);
23188b09229SIker Pedrosa 
st7920_power_on(struct st7920_device * st7920,struct spi7920_error * err)23288b09229SIker Pedrosa static void st7920_power_on(struct st7920_device *st7920,
23388b09229SIker Pedrosa 			    struct spi7920_error *err)
23488b09229SIker Pedrosa {
23588b09229SIker Pedrosa 	st7920_spi_write(st7920->spi, SET_DISPLAY_ON, NULL, 72, err);
23688b09229SIker Pedrosa }
23788b09229SIker Pedrosa 
st7920_power_off(struct st7920_device * st7920,struct spi7920_error * err)23888b09229SIker Pedrosa static void st7920_power_off(struct st7920_device *st7920,
23988b09229SIker Pedrosa 			     struct spi7920_error *err)
24088b09229SIker Pedrosa {
24188b09229SIker Pedrosa 	st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, err);
24288b09229SIker Pedrosa 	st7920_spi_write(st7920->spi, SET_DISPLAY_OFF, NULL, 72, err);
24388b09229SIker Pedrosa }
24488b09229SIker Pedrosa 
st7920_hw_reset(struct st7920_device * st7920)24588b09229SIker Pedrosa static void st7920_hw_reset(struct st7920_device *st7920)
24688b09229SIker Pedrosa {
24788b09229SIker Pedrosa 	if (!st7920->reset_gpio)
24888b09229SIker Pedrosa 		return;
24988b09229SIker Pedrosa 
25088b09229SIker Pedrosa 	gpiod_set_value_cansleep(st7920->reset_gpio, 1);
25188b09229SIker Pedrosa 	usleep_range(15, 20);
25288b09229SIker Pedrosa 	gpiod_set_value_cansleep(st7920->reset_gpio, 0);
25388b09229SIker Pedrosa 	msleep(40);
25488b09229SIker Pedrosa }
25588b09229SIker Pedrosa 
st7920_init(struct st7920_device * st7920)25688b09229SIker Pedrosa static int st7920_init(struct st7920_device *st7920)
25788b09229SIker Pedrosa {
25888b09229SIker Pedrosa 	struct spi7920_error err = {0};
25988b09229SIker Pedrosa 
26088b09229SIker Pedrosa 	st7920_spi_write(st7920->spi, SET_BASIC_INSTRUCTION_SET, NULL, 72, &err);
26188b09229SIker Pedrosa 	st7920_power_on(st7920, &err);
26288b09229SIker Pedrosa 	st7920_spi_write(st7920->spi, SET_GRAPHICS_DISPLAY, NULL, 72, &err);
26388b09229SIker Pedrosa 	st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, &err);
26488b09229SIker Pedrosa 
26588b09229SIker Pedrosa 	return err.errno;
26688b09229SIker Pedrosa }
26788b09229SIker Pedrosa 
st7920_update_rect(struct st7920_device * st7920,struct drm_rect * rect,u8 * buf,u8 * data_array)26888b09229SIker Pedrosa static int st7920_update_rect(struct st7920_device *st7920,
26988b09229SIker Pedrosa 			      struct drm_rect *rect, u8 *buf,
27088b09229SIker Pedrosa 			      u8 *data_array)
27188b09229SIker Pedrosa {
27288b09229SIker Pedrosa 	struct spi7920_error err = {0};
27388b09229SIker Pedrosa 	u32 array_idx = 0;
27488b09229SIker Pedrosa 	int i, j;
27588b09229SIker Pedrosa 
27688b09229SIker Pedrosa 	/*
27788b09229SIker Pedrosa 	 * The screen is divided in 64(Y)x8(X) segments and each segment is
27888b09229SIker Pedrosa 	 * further divided in 2 bytes (D15~D0).
27988b09229SIker Pedrosa 	 *
28088b09229SIker Pedrosa 	 * Segment 0x0 is in the top-right corner, while segment 63x15 is in the
28188b09229SIker Pedrosa 	 * bottom-left. They would be displayed in the screen in the following way:
28288b09229SIker Pedrosa 	 * 0x0  0x1  0x2  ... 0x15
28388b09229SIker Pedrosa 	 * 1x0  1x1  1x2  ... 1x15
28488b09229SIker Pedrosa 	 * ...
28588b09229SIker Pedrosa 	 * 63x0 63x1 63x2 ... 63x15
28688b09229SIker Pedrosa 	 *
28788b09229SIker Pedrosa 	 * The data in each byte is big endian.
28888b09229SIker Pedrosa 	 */
28988b09229SIker Pedrosa 
29088b09229SIker Pedrosa 	for (i = 0; i < ST7920_SCANLINES; i++) {
29188b09229SIker Pedrosa 		u8 *line_start = buf + (i * ST7920_PITCH);
29288b09229SIker Pedrosa 		u8 line_buffer[ST7920_PITCH];
29388b09229SIker Pedrosa 
29488b09229SIker Pedrosa 		for (j = 0; j < ST7920_PITCH; j++) {
29588b09229SIker Pedrosa 			line_buffer[j] = bitrev8(line_start[j]);
29688b09229SIker Pedrosa 			data_array[array_idx++] = line_buffer[j];
29788b09229SIker Pedrosa 		}
29888b09229SIker Pedrosa 
29988b09229SIker Pedrosa 		st7920_spi_write(st7920->spi, SET_GDRAM_ADDRESS, &i, 72, &err);
30088b09229SIker Pedrosa 		st7920_spi_write(st7920->spi, SET_GDRAM_DATA, line_buffer, 72, &err);
30188b09229SIker Pedrosa 	}
30288b09229SIker Pedrosa 
30388b09229SIker Pedrosa 	return err.errno;
30488b09229SIker Pedrosa }
30588b09229SIker Pedrosa 
st7920_clear_screen(struct st7920_device * st7920,u8 * data_array)30688b09229SIker Pedrosa static void st7920_clear_screen(struct st7920_device *st7920, u8 *data_array)
30788b09229SIker Pedrosa {
30888b09229SIker Pedrosa 	struct spi7920_error err = {0};
30988b09229SIker Pedrosa 
31088b09229SIker Pedrosa 	memset(data_array, 0, BYTES_IN_DISPLAY);
31188b09229SIker Pedrosa 
31288b09229SIker Pedrosa 	st7920_spi_write(st7920->spi, SET_DISPLAY_CLEAR, NULL, 1600, &err);
31388b09229SIker Pedrosa }
31488b09229SIker Pedrosa 
st7920_fb_blit_rect(struct drm_framebuffer * fb,const struct iosys_map * vmap,struct drm_rect * rect,u8 * buf,u8 * data_array,struct drm_format_conv_state * fmtcnv_state)31588b09229SIker Pedrosa static int st7920_fb_blit_rect(struct drm_framebuffer *fb,
31688b09229SIker Pedrosa 			       const struct iosys_map *vmap,
31788b09229SIker Pedrosa 			       struct drm_rect *rect,
31888b09229SIker Pedrosa 			       u8 *buf, u8 *data_array,
31988b09229SIker Pedrosa 			       struct drm_format_conv_state *fmtcnv_state)
32088b09229SIker Pedrosa {
32188b09229SIker Pedrosa 	struct st7920_device *st7920 = drm_to_st7920(fb->dev);
32288b09229SIker Pedrosa 	struct iosys_map dst;
32388b09229SIker Pedrosa 	unsigned int dst_pitch;
32488b09229SIker Pedrosa 	int ret;
32588b09229SIker Pedrosa 
32688b09229SIker Pedrosa 	/* Align y to display page boundaries */
32788b09229SIker Pedrosa 	rect->y1 = round_down(rect->y1, PIXELS_PER_SEGMENT);
32888b09229SIker Pedrosa 	rect->y2 = min_t(unsigned int, round_up(rect->y2, PIXELS_PER_SEGMENT), st7920->height);
32988b09229SIker Pedrosa 
33088b09229SIker Pedrosa 	dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
33188b09229SIker Pedrosa 
33288b09229SIker Pedrosa 	iosys_map_set_vaddr(&dst, buf);
33388b09229SIker Pedrosa 	drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
33488b09229SIker Pedrosa 
33588b09229SIker Pedrosa 	ret = st7920_update_rect(st7920, rect, buf, data_array);
33688b09229SIker Pedrosa 
33788b09229SIker Pedrosa 	return ret;
33888b09229SIker Pedrosa }
33988b09229SIker Pedrosa 
st7920_primary_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)34088b09229SIker Pedrosa static int st7920_primary_plane_atomic_check(struct drm_plane *plane,
34188b09229SIker Pedrosa 					     struct drm_atomic_state *state)
34288b09229SIker Pedrosa {
34388b09229SIker Pedrosa 	struct drm_device *drm = plane->dev;
34488b09229SIker Pedrosa 	struct st7920_device *st7920 = drm_to_st7920(drm);
34588b09229SIker Pedrosa 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
34688b09229SIker Pedrosa 	struct st7920_plane_state *st7920_state = to_st7920_plane_state(plane_state);
34788b09229SIker Pedrosa 	struct drm_shadow_plane_state *shadow_plane_state = &st7920_state->base;
34888b09229SIker Pedrosa 	struct drm_crtc *crtc = plane_state->crtc;
34988b09229SIker Pedrosa 	struct drm_crtc_state *crtc_state = NULL;
35088b09229SIker Pedrosa 	const struct drm_format_info *fi;
35188b09229SIker Pedrosa 	unsigned int pitch;
35288b09229SIker Pedrosa 	int ret;
35388b09229SIker Pedrosa 
35488b09229SIker Pedrosa 	if (crtc)
35588b09229SIker Pedrosa 		crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
35688b09229SIker Pedrosa 
35788b09229SIker Pedrosa 	ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
35888b09229SIker Pedrosa 						  DRM_PLANE_NO_SCALING,
35988b09229SIker Pedrosa 						  DRM_PLANE_NO_SCALING,
36088b09229SIker Pedrosa 						  false, false);
36188b09229SIker Pedrosa 	if (ret)
36288b09229SIker Pedrosa 		return ret;
36388b09229SIker Pedrosa 	else if (!plane_state->visible)
36488b09229SIker Pedrosa 		return 0;
36588b09229SIker Pedrosa 
36688b09229SIker Pedrosa 	fi = drm_format_info(DRM_FORMAT_R1);
36788b09229SIker Pedrosa 	if (!fi)
36888b09229SIker Pedrosa 		return -EINVAL;
36988b09229SIker Pedrosa 
37088b09229SIker Pedrosa 	pitch = drm_format_info_min_pitch(fi, 0, st7920->width);
37188b09229SIker Pedrosa 
37288b09229SIker Pedrosa 	if (plane_state->fb->format != fi) {
37388b09229SIker Pedrosa 		void *buf;
37488b09229SIker Pedrosa 
37588b09229SIker Pedrosa 		/* format conversion necessary; reserve buffer */
37688b09229SIker Pedrosa 		buf = drm_format_conv_state_reserve(&shadow_plane_state->fmtcnv_state,
37788b09229SIker Pedrosa 						    pitch, GFP_KERNEL);
37888b09229SIker Pedrosa 		if (!buf)
37988b09229SIker Pedrosa 			return -ENOMEM;
38088b09229SIker Pedrosa 	}
38188b09229SIker Pedrosa 
38288b09229SIker Pedrosa 	st7920_state->buffer = kcalloc(pitch, st7920->height, GFP_KERNEL);
38388b09229SIker Pedrosa 	if (!st7920_state->buffer)
38488b09229SIker Pedrosa 		return -ENOMEM;
38588b09229SIker Pedrosa 
38688b09229SIker Pedrosa 	return 0;
38788b09229SIker Pedrosa }
38888b09229SIker Pedrosa 
st7920_primary_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)38988b09229SIker Pedrosa static void st7920_primary_plane_atomic_update(struct drm_plane *plane,
39088b09229SIker Pedrosa 					       struct drm_atomic_state *state)
39188b09229SIker Pedrosa {
39288b09229SIker Pedrosa 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
39388b09229SIker Pedrosa 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
39488b09229SIker Pedrosa 	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
39588b09229SIker Pedrosa 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
39688b09229SIker Pedrosa 	struct st7920_crtc_state *st7920_crtc_state =  to_st7920_crtc_state(crtc_state);
39788b09229SIker Pedrosa 	struct st7920_plane_state *st7920_plane_state = to_st7920_plane_state(plane_state);
39888b09229SIker Pedrosa 	struct drm_framebuffer *fb = plane_state->fb;
39988b09229SIker Pedrosa 	struct drm_atomic_helper_damage_iter iter;
40088b09229SIker Pedrosa 	struct drm_device *drm = plane->dev;
40188b09229SIker Pedrosa 	struct drm_rect dst_clip;
40288b09229SIker Pedrosa 	struct drm_rect damage;
40388b09229SIker Pedrosa 	int idx;
40488b09229SIker Pedrosa 	int ret;
40588b09229SIker Pedrosa 
40688b09229SIker Pedrosa 	if (!drm_dev_enter(drm, &idx))
40788b09229SIker Pedrosa 		return;
40888b09229SIker Pedrosa 
40988b09229SIker Pedrosa 	if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
41088b09229SIker Pedrosa 		drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
41188b09229SIker Pedrosa 		drm_atomic_for_each_plane_damage(&iter, &damage) {
41288b09229SIker Pedrosa 			dst_clip = plane_state->dst;
41388b09229SIker Pedrosa 
41488b09229SIker Pedrosa 			if (!drm_rect_intersect(&dst_clip, &damage))
41588b09229SIker Pedrosa 				continue;
41688b09229SIker Pedrosa 
41788b09229SIker Pedrosa 			ret = st7920_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip,
41888b09229SIker Pedrosa 						  st7920_plane_state->buffer,
41988b09229SIker Pedrosa 						  st7920_crtc_state->data_array,
42088b09229SIker Pedrosa 						  &shadow_plane_state->fmtcnv_state);
42188b09229SIker Pedrosa 			if (ret)
42288b09229SIker Pedrosa 				drm_err_once(plane->dev, "Failed to write to device: %d.\n", ret);
42388b09229SIker Pedrosa 		}
42488b09229SIker Pedrosa 
42588b09229SIker Pedrosa 		drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
42688b09229SIker Pedrosa 	}
42788b09229SIker Pedrosa 
42888b09229SIker Pedrosa 	drm_dev_exit(idx);
42988b09229SIker Pedrosa }
43088b09229SIker Pedrosa 
st7920_primary_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)43188b09229SIker Pedrosa static void st7920_primary_plane_atomic_disable(struct drm_plane *plane,
43288b09229SIker Pedrosa 						struct drm_atomic_state *state)
43388b09229SIker Pedrosa {
43488b09229SIker Pedrosa 	struct drm_device *drm = plane->dev;
43588b09229SIker Pedrosa 	struct st7920_device *st7920 = drm_to_st7920(drm);
43688b09229SIker Pedrosa 	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
43788b09229SIker Pedrosa 	struct drm_crtc_state *crtc_state;
43888b09229SIker Pedrosa 	struct st7920_crtc_state *st7920_crtc_state;
43988b09229SIker Pedrosa 	int idx;
44088b09229SIker Pedrosa 
44188b09229SIker Pedrosa 	if (!plane_state->crtc)
44288b09229SIker Pedrosa 		return;
44388b09229SIker Pedrosa 
44488b09229SIker Pedrosa 	crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
44588b09229SIker Pedrosa 	st7920_crtc_state = to_st7920_crtc_state(crtc_state);
44688b09229SIker Pedrosa 
44788b09229SIker Pedrosa 	if (!drm_dev_enter(drm, &idx))
44888b09229SIker Pedrosa 		return;
44988b09229SIker Pedrosa 
45088b09229SIker Pedrosa 	st7920_clear_screen(st7920, st7920_crtc_state->data_array);
45188b09229SIker Pedrosa 
45288b09229SIker Pedrosa 	drm_dev_exit(idx);
45388b09229SIker Pedrosa }
45488b09229SIker Pedrosa 
45588b09229SIker Pedrosa /* Called during init to allocate the plane's atomic state. */
st7920_primary_plane_reset(struct drm_plane * plane)45688b09229SIker Pedrosa static void st7920_primary_plane_reset(struct drm_plane *plane)
45788b09229SIker Pedrosa {
45888b09229SIker Pedrosa 	struct st7920_plane_state *st7920_state;
45988b09229SIker Pedrosa 
46088b09229SIker Pedrosa 	drm_WARN_ON_ONCE(plane->dev, plane->state);
46188b09229SIker Pedrosa 
462*bf4afc53SLinus Torvalds 	st7920_state = kzalloc_obj(*st7920_state);
46388b09229SIker Pedrosa 	if (!st7920_state)
46488b09229SIker Pedrosa 		return;
46588b09229SIker Pedrosa 
46688b09229SIker Pedrosa 	__drm_gem_reset_shadow_plane(plane, &st7920_state->base);
46788b09229SIker Pedrosa }
46888b09229SIker Pedrosa 
st7920_primary_plane_duplicate_state(struct drm_plane * plane)46988b09229SIker Pedrosa static struct drm_plane_state *st7920_primary_plane_duplicate_state(struct drm_plane *plane)
47088b09229SIker Pedrosa {
47188b09229SIker Pedrosa 	struct drm_shadow_plane_state *new_shadow_plane_state;
47288b09229SIker Pedrosa 	struct st7920_plane_state *st7920_state;
47388b09229SIker Pedrosa 
47488b09229SIker Pedrosa 	if (drm_WARN_ON_ONCE(plane->dev, !plane->state))
47588b09229SIker Pedrosa 		return NULL;
47688b09229SIker Pedrosa 
477*bf4afc53SLinus Torvalds 	st7920_state = kzalloc_obj(*st7920_state);
47888b09229SIker Pedrosa 	if (!st7920_state)
47988b09229SIker Pedrosa 		return NULL;
48088b09229SIker Pedrosa 
48188b09229SIker Pedrosa 	new_shadow_plane_state = &st7920_state->base;
48288b09229SIker Pedrosa 
48388b09229SIker Pedrosa 	__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
48488b09229SIker Pedrosa 
48588b09229SIker Pedrosa 	return &new_shadow_plane_state->base;
48688b09229SIker Pedrosa }
48788b09229SIker Pedrosa 
st7920_primary_plane_destroy_state(struct drm_plane * plane,struct drm_plane_state * state)48888b09229SIker Pedrosa static void st7920_primary_plane_destroy_state(struct drm_plane *plane,
48988b09229SIker Pedrosa 					       struct drm_plane_state *state)
49088b09229SIker Pedrosa {
49188b09229SIker Pedrosa 	struct st7920_plane_state *st7920_state = to_st7920_plane_state(state);
49288b09229SIker Pedrosa 
49388b09229SIker Pedrosa 	kfree(st7920_state->buffer);
49488b09229SIker Pedrosa 
49588b09229SIker Pedrosa 	__drm_gem_destroy_shadow_plane_state(&st7920_state->base);
49688b09229SIker Pedrosa 
49788b09229SIker Pedrosa 	kfree(st7920_state);
49888b09229SIker Pedrosa }
49988b09229SIker Pedrosa 
50088b09229SIker Pedrosa static const struct drm_plane_helper_funcs st7920_primary_plane_helper_funcs = {
50188b09229SIker Pedrosa 	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
50288b09229SIker Pedrosa 	.atomic_check = st7920_primary_plane_atomic_check,
50388b09229SIker Pedrosa 	.atomic_update = st7920_primary_plane_atomic_update,
50488b09229SIker Pedrosa 	.atomic_disable = st7920_primary_plane_atomic_disable,
50588b09229SIker Pedrosa };
50688b09229SIker Pedrosa 
50788b09229SIker Pedrosa static const struct drm_plane_funcs st7920_primary_plane_funcs = {
50888b09229SIker Pedrosa 	.update_plane = drm_atomic_helper_update_plane,
50988b09229SIker Pedrosa 	.disable_plane = drm_atomic_helper_disable_plane,
51088b09229SIker Pedrosa 	.reset = st7920_primary_plane_reset,
51188b09229SIker Pedrosa 	.atomic_duplicate_state = st7920_primary_plane_duplicate_state,
51288b09229SIker Pedrosa 	.atomic_destroy_state = st7920_primary_plane_destroy_state,
51388b09229SIker Pedrosa 	.destroy = drm_plane_cleanup,
51488b09229SIker Pedrosa };
51588b09229SIker Pedrosa 
st7920_crtc_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)51688b09229SIker Pedrosa static enum drm_mode_status st7920_crtc_mode_valid(struct drm_crtc *crtc,
51788b09229SIker Pedrosa 						   const struct drm_display_mode *mode)
51888b09229SIker Pedrosa {
51988b09229SIker Pedrosa 	struct st7920_device *st7920 = drm_to_st7920(crtc->dev);
52088b09229SIker Pedrosa 
52188b09229SIker Pedrosa 	return drm_crtc_helper_mode_valid_fixed(crtc, mode, &st7920->mode);
52288b09229SIker Pedrosa }
52388b09229SIker Pedrosa 
st7920_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)52488b09229SIker Pedrosa static int st7920_crtc_atomic_check(struct drm_crtc *crtc,
52588b09229SIker Pedrosa 				    struct drm_atomic_state *state)
52688b09229SIker Pedrosa {
52788b09229SIker Pedrosa 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
52888b09229SIker Pedrosa 	struct st7920_crtc_state *st7920_state = to_st7920_crtc_state(crtc_state);
52988b09229SIker Pedrosa 	int ret;
53088b09229SIker Pedrosa 
53188b09229SIker Pedrosa 	ret = drm_crtc_helper_atomic_check(crtc, state);
53288b09229SIker Pedrosa 	if (ret)
53388b09229SIker Pedrosa 		return ret;
53488b09229SIker Pedrosa 
53588b09229SIker Pedrosa 	st7920_state->data_array = kmalloc(BYTES_IN_DISPLAY, GFP_KERNEL);
53688b09229SIker Pedrosa 	if (!st7920_state->data_array)
53788b09229SIker Pedrosa 		return -ENOMEM;
53888b09229SIker Pedrosa 
53988b09229SIker Pedrosa 	return 0;
54088b09229SIker Pedrosa }
54188b09229SIker Pedrosa 
st7920_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)54288b09229SIker Pedrosa static void st7920_crtc_atomic_enable(struct drm_crtc *crtc,
54388b09229SIker Pedrosa 				      struct drm_atomic_state *state)
54488b09229SIker Pedrosa {
54588b09229SIker Pedrosa 	struct drm_device *drm = crtc->dev;
54688b09229SIker Pedrosa 	struct st7920_device *st7920 = drm_to_st7920(drm);
54788b09229SIker Pedrosa 	int idx;
54888b09229SIker Pedrosa 	int ret;
54988b09229SIker Pedrosa 
55088b09229SIker Pedrosa 	if (!drm_dev_enter(drm, &idx))
55188b09229SIker Pedrosa 		return;
55288b09229SIker Pedrosa 
55388b09229SIker Pedrosa 	st7920_hw_reset(st7920);
55488b09229SIker Pedrosa 
55588b09229SIker Pedrosa 	ret = st7920_init(st7920);
55688b09229SIker Pedrosa 	if (ret)
55788b09229SIker Pedrosa 		drm_err(drm, "Failed to init hardware: %d\n", ret);
55888b09229SIker Pedrosa 
55988b09229SIker Pedrosa 	drm_dev_exit(idx);
56088b09229SIker Pedrosa }
56188b09229SIker Pedrosa 
st7920_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)56288b09229SIker Pedrosa static void st7920_crtc_atomic_disable(struct drm_crtc *crtc,
56388b09229SIker Pedrosa 				       struct drm_atomic_state *state)
56488b09229SIker Pedrosa {
56588b09229SIker Pedrosa 	struct spi7920_error err = {0};
56688b09229SIker Pedrosa 	struct drm_device *drm = crtc->dev;
56788b09229SIker Pedrosa 	struct st7920_device *st7920 = drm_to_st7920(drm);
56888b09229SIker Pedrosa 	int idx;
56988b09229SIker Pedrosa 
57088b09229SIker Pedrosa 	drm_dev_enter(drm, &idx);
57188b09229SIker Pedrosa 
57288b09229SIker Pedrosa 	st7920_power_off(st7920, &err);
57388b09229SIker Pedrosa 
57488b09229SIker Pedrosa 	drm_dev_exit(idx);
57588b09229SIker Pedrosa }
57688b09229SIker Pedrosa 
57788b09229SIker Pedrosa /* Called during init to allocate the CRTC's atomic state. */
st7920_crtc_reset(struct drm_crtc * crtc)57888b09229SIker Pedrosa static void st7920_crtc_reset(struct drm_crtc *crtc)
57988b09229SIker Pedrosa {
58088b09229SIker Pedrosa 	struct st7920_crtc_state *st7920_state;
58188b09229SIker Pedrosa 
58288b09229SIker Pedrosa 	drm_WARN_ON_ONCE(crtc->dev, crtc->state);
58388b09229SIker Pedrosa 
584*bf4afc53SLinus Torvalds 	st7920_state = kzalloc_obj(*st7920_state);
58588b09229SIker Pedrosa 	if (!st7920_state)
58688b09229SIker Pedrosa 		return;
58788b09229SIker Pedrosa 
58888b09229SIker Pedrosa 	__drm_atomic_helper_crtc_reset(crtc, &st7920_state->base);
58988b09229SIker Pedrosa }
59088b09229SIker Pedrosa 
st7920_crtc_duplicate_state(struct drm_crtc * crtc)59188b09229SIker Pedrosa static struct drm_crtc_state *st7920_crtc_duplicate_state(struct drm_crtc *crtc)
59288b09229SIker Pedrosa {
59388b09229SIker Pedrosa 	struct st7920_crtc_state *st7920_state;
59488b09229SIker Pedrosa 
59588b09229SIker Pedrosa 	if (drm_WARN_ON_ONCE(crtc->dev, !crtc->state))
59688b09229SIker Pedrosa 		return NULL;
59788b09229SIker Pedrosa 
598*bf4afc53SLinus Torvalds 	st7920_state = kzalloc_obj(*st7920_state);
59988b09229SIker Pedrosa 	if (!st7920_state)
60088b09229SIker Pedrosa 		return NULL;
60188b09229SIker Pedrosa 
60288b09229SIker Pedrosa 	__drm_atomic_helper_crtc_duplicate_state(crtc, &st7920_state->base);
60388b09229SIker Pedrosa 
60488b09229SIker Pedrosa 	return &st7920_state->base;
60588b09229SIker Pedrosa }
60688b09229SIker Pedrosa 
st7920_crtc_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)60788b09229SIker Pedrosa static void st7920_crtc_destroy_state(struct drm_crtc *crtc,
60888b09229SIker Pedrosa 				      struct drm_crtc_state *state)
60988b09229SIker Pedrosa {
61088b09229SIker Pedrosa 	struct st7920_crtc_state *st7920_state = to_st7920_crtc_state(state);
61188b09229SIker Pedrosa 
61288b09229SIker Pedrosa 	kfree(st7920_state->data_array);
61388b09229SIker Pedrosa 
61488b09229SIker Pedrosa 	__drm_atomic_helper_crtc_destroy_state(state);
61588b09229SIker Pedrosa 
61688b09229SIker Pedrosa 	kfree(st7920_state);
61788b09229SIker Pedrosa }
61888b09229SIker Pedrosa 
61988b09229SIker Pedrosa /*
62088b09229SIker Pedrosa  * The CRTC is always enabled. Screen updates are performed by
62188b09229SIker Pedrosa  * the primary plane's atomic_update function. Disabling clears
62288b09229SIker Pedrosa  * the screen in the primary plane's atomic_disable function.
62388b09229SIker Pedrosa  */
62488b09229SIker Pedrosa static const struct drm_crtc_helper_funcs st7920_crtc_helper_funcs = {
62588b09229SIker Pedrosa 	.mode_valid = st7920_crtc_mode_valid,
62688b09229SIker Pedrosa 	.atomic_check = st7920_crtc_atomic_check,
62788b09229SIker Pedrosa 	.atomic_enable = st7920_crtc_atomic_enable,
62888b09229SIker Pedrosa 	.atomic_disable = st7920_crtc_atomic_disable,
62988b09229SIker Pedrosa };
63088b09229SIker Pedrosa 
63188b09229SIker Pedrosa static const struct drm_crtc_funcs st7920_crtc_funcs = {
63288b09229SIker Pedrosa 	.reset = st7920_crtc_reset,
63388b09229SIker Pedrosa 	.destroy = drm_crtc_cleanup,
63488b09229SIker Pedrosa 	.set_config = drm_atomic_helper_set_config,
63588b09229SIker Pedrosa 	.page_flip = drm_atomic_helper_page_flip,
63688b09229SIker Pedrosa 	.atomic_duplicate_state = st7920_crtc_duplicate_state,
63788b09229SIker Pedrosa 	.atomic_destroy_state = st7920_crtc_destroy_state,
63888b09229SIker Pedrosa };
63988b09229SIker Pedrosa 
64088b09229SIker Pedrosa static const struct drm_encoder_funcs st7920_encoder_funcs = {
64188b09229SIker Pedrosa 	.destroy = drm_encoder_cleanup,
64288b09229SIker Pedrosa };
64388b09229SIker Pedrosa 
st7920_connector_get_modes(struct drm_connector * connector)64488b09229SIker Pedrosa static int st7920_connector_get_modes(struct drm_connector *connector)
64588b09229SIker Pedrosa {
64688b09229SIker Pedrosa 	struct st7920_device *st7920 = drm_to_st7920(connector->dev);
64788b09229SIker Pedrosa 
64888b09229SIker Pedrosa 	return drm_connector_helper_get_modes_fixed(connector, &st7920->mode);
64988b09229SIker Pedrosa }
65088b09229SIker Pedrosa 
65188b09229SIker Pedrosa static const struct drm_connector_helper_funcs st7920_connector_helper_funcs = {
65288b09229SIker Pedrosa 	.get_modes = st7920_connector_get_modes,
65388b09229SIker Pedrosa };
65488b09229SIker Pedrosa 
65588b09229SIker Pedrosa static const struct drm_connector_funcs st7920_connector_funcs = {
65688b09229SIker Pedrosa 	.reset = drm_atomic_helper_connector_reset,
65788b09229SIker Pedrosa 	.fill_modes = drm_helper_probe_single_connector_modes,
65888b09229SIker Pedrosa 	.destroy = drm_connector_cleanup,
65988b09229SIker Pedrosa 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
66088b09229SIker Pedrosa 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
66188b09229SIker Pedrosa };
66288b09229SIker Pedrosa 
66388b09229SIker Pedrosa static const struct drm_mode_config_funcs st7920_mode_config_funcs = {
66488b09229SIker Pedrosa 	.fb_create = drm_gem_fb_create_with_dirty,
66588b09229SIker Pedrosa 	.atomic_check = drm_atomic_helper_check,
66688b09229SIker Pedrosa 	.atomic_commit = drm_atomic_helper_commit,
66788b09229SIker Pedrosa };
66888b09229SIker Pedrosa 
66988b09229SIker Pedrosa static const uint32_t st7920_formats[] = {
67088b09229SIker Pedrosa 	DRM_FORMAT_XRGB8888,
67188b09229SIker Pedrosa };
67288b09229SIker Pedrosa 
67388b09229SIker Pedrosa DEFINE_DRM_GEM_FOPS(st7920_fops);
67488b09229SIker Pedrosa 
67588b09229SIker Pedrosa static const struct drm_driver st7920_drm_driver = {
67688b09229SIker Pedrosa 	DRM_GEM_SHMEM_DRIVER_OPS,
67788b09229SIker Pedrosa 	DRM_FBDEV_SHMEM_DRIVER_OPS,
67888b09229SIker Pedrosa 	.name			= DRIVER_NAME,
67988b09229SIker Pedrosa 	.desc			= DRIVER_DESC,
68088b09229SIker Pedrosa 	.major			= DRIVER_MAJOR,
68188b09229SIker Pedrosa 	.minor			= DRIVER_MINOR,
68288b09229SIker Pedrosa 	.driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
68388b09229SIker Pedrosa 	.fops			= &st7920_fops,
68488b09229SIker Pedrosa };
68588b09229SIker Pedrosa 
st7920_init_modeset(struct st7920_device * st7920)68688b09229SIker Pedrosa static int st7920_init_modeset(struct st7920_device *st7920)
68788b09229SIker Pedrosa {
68888b09229SIker Pedrosa 	struct drm_display_mode *mode = &st7920->mode;
68988b09229SIker Pedrosa 	struct drm_device *drm = &st7920->drm;
69088b09229SIker Pedrosa 	unsigned long max_width, max_height;
69188b09229SIker Pedrosa 	struct drm_plane *primary_plane;
69288b09229SIker Pedrosa 	struct drm_crtc *crtc;
69388b09229SIker Pedrosa 	struct drm_encoder *encoder;
69488b09229SIker Pedrosa 	struct drm_connector *connector;
69588b09229SIker Pedrosa 	int ret;
69688b09229SIker Pedrosa 
69788b09229SIker Pedrosa 	/*
69888b09229SIker Pedrosa 	 * Modesetting
69988b09229SIker Pedrosa 	 */
70088b09229SIker Pedrosa 
70188b09229SIker Pedrosa 	ret = drmm_mode_config_init(drm);
70288b09229SIker Pedrosa 	if (ret) {
70388b09229SIker Pedrosa 		drm_err(drm, "DRM mode config init failed: %d\n", ret);
70488b09229SIker Pedrosa 		return ret;
70588b09229SIker Pedrosa 	}
70688b09229SIker Pedrosa 
70788b09229SIker Pedrosa 	mode->type = DRM_MODE_TYPE_DRIVER;
70888b09229SIker Pedrosa 	mode->clock = 30;
70988b09229SIker Pedrosa 	mode->hdisplay = st7920->width;
71088b09229SIker Pedrosa 	mode->htotal = st7920->width;
71188b09229SIker Pedrosa 	mode->hsync_start = st7920->width;
71288b09229SIker Pedrosa 	mode->hsync_end = st7920->width;
71388b09229SIker Pedrosa 	mode->vdisplay = st7920->height;
71488b09229SIker Pedrosa 	mode->vtotal = st7920->height;
71588b09229SIker Pedrosa 	mode->vsync_start = st7920->height;
71688b09229SIker Pedrosa 	mode->vsync_end = st7920->height;
71788b09229SIker Pedrosa 	mode->width_mm = 27;
71888b09229SIker Pedrosa 	mode->height_mm = 27;
71988b09229SIker Pedrosa 
72088b09229SIker Pedrosa 	max_width = max_t(unsigned long, mode->hdisplay, DRM_SHADOW_PLANE_MAX_WIDTH);
72188b09229SIker Pedrosa 	max_height = max_t(unsigned long, mode->vdisplay, DRM_SHADOW_PLANE_MAX_HEIGHT);
72288b09229SIker Pedrosa 
72388b09229SIker Pedrosa 	drm->mode_config.min_width = mode->hdisplay;
72488b09229SIker Pedrosa 	drm->mode_config.max_width = max_width;
72588b09229SIker Pedrosa 	drm->mode_config.min_height = mode->vdisplay;
72688b09229SIker Pedrosa 	drm->mode_config.max_height = max_height;
72788b09229SIker Pedrosa 	drm->mode_config.preferred_depth = 24;
72888b09229SIker Pedrosa 	drm->mode_config.funcs = &st7920_mode_config_funcs;
72988b09229SIker Pedrosa 
73088b09229SIker Pedrosa 	/* Primary plane */
73188b09229SIker Pedrosa 
73288b09229SIker Pedrosa 	primary_plane = &st7920->primary_plane;
73388b09229SIker Pedrosa 	ret = drm_universal_plane_init(drm, primary_plane, 0, &st7920_primary_plane_funcs,
73488b09229SIker Pedrosa 				       st7920_formats, ARRAY_SIZE(st7920_formats),
73588b09229SIker Pedrosa 				       NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
73688b09229SIker Pedrosa 	if (ret) {
73788b09229SIker Pedrosa 		drm_err(drm, "DRM primary plane init failed: %d\n", ret);
73888b09229SIker Pedrosa 		return ret;
73988b09229SIker Pedrosa 	}
74088b09229SIker Pedrosa 
74188b09229SIker Pedrosa 	drm_plane_helper_add(primary_plane, &st7920_primary_plane_helper_funcs);
74288b09229SIker Pedrosa 
74388b09229SIker Pedrosa 	drm_plane_enable_fb_damage_clips(primary_plane);
74488b09229SIker Pedrosa 
74588b09229SIker Pedrosa 	/* CRTC */
74688b09229SIker Pedrosa 
74788b09229SIker Pedrosa 	crtc = &st7920->crtc;
74888b09229SIker Pedrosa 	ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
74988b09229SIker Pedrosa 					&st7920_crtc_funcs, NULL);
75088b09229SIker Pedrosa 	if (ret) {
75188b09229SIker Pedrosa 		drm_err(drm, "DRM crtc init failed: %d\n", ret);
75288b09229SIker Pedrosa 		return ret;
75388b09229SIker Pedrosa 	}
75488b09229SIker Pedrosa 
75588b09229SIker Pedrosa 	drm_crtc_helper_add(crtc, &st7920_crtc_helper_funcs);
75688b09229SIker Pedrosa 
75788b09229SIker Pedrosa 	/* Encoder */
75888b09229SIker Pedrosa 
75988b09229SIker Pedrosa 	encoder = &st7920->encoder;
76088b09229SIker Pedrosa 	ret = drm_encoder_init(drm, encoder, &st7920_encoder_funcs,
76188b09229SIker Pedrosa 			       DRM_MODE_ENCODER_NONE, NULL);
76288b09229SIker Pedrosa 	if (ret) {
76388b09229SIker Pedrosa 		drm_err(drm, "DRM encoder init failed: %d\n", ret);
76488b09229SIker Pedrosa 		return ret;
76588b09229SIker Pedrosa 	}
76688b09229SIker Pedrosa 
76788b09229SIker Pedrosa 	encoder->possible_crtcs = drm_crtc_mask(crtc);
76888b09229SIker Pedrosa 
76988b09229SIker Pedrosa 	/* Connector */
77088b09229SIker Pedrosa 
77188b09229SIker Pedrosa 	connector = &st7920->connector;
77288b09229SIker Pedrosa 	ret = drm_connector_init(drm, connector, &st7920_connector_funcs,
77388b09229SIker Pedrosa 				 DRM_MODE_CONNECTOR_Unknown);
77488b09229SIker Pedrosa 	if (ret) {
77588b09229SIker Pedrosa 		drm_err(drm, "DRM connector init failed: %d\n", ret);
77688b09229SIker Pedrosa 		return ret;
77788b09229SIker Pedrosa 	}
77888b09229SIker Pedrosa 
77988b09229SIker Pedrosa 	drm_connector_helper_add(connector, &st7920_connector_helper_funcs);
78088b09229SIker Pedrosa 
78188b09229SIker Pedrosa 	ret = drm_connector_attach_encoder(connector, encoder);
78288b09229SIker Pedrosa 	if (ret) {
78388b09229SIker Pedrosa 		drm_err(drm, "DRM attach connector to encoder failed: %d\n", ret);
78488b09229SIker Pedrosa 		return ret;
78588b09229SIker Pedrosa 	}
78688b09229SIker Pedrosa 
78788b09229SIker Pedrosa 	drm_mode_config_reset(drm);
78888b09229SIker Pedrosa 
78988b09229SIker Pedrosa 	return 0;
79088b09229SIker Pedrosa }
79188b09229SIker Pedrosa 
st7920_probe(struct spi_device * spi)79288b09229SIker Pedrosa static int st7920_probe(struct spi_device *spi)
79388b09229SIker Pedrosa {
79488b09229SIker Pedrosa 	struct st7920_device *st7920;
79588b09229SIker Pedrosa 	struct regmap *regmap;
79688b09229SIker Pedrosa 	struct device *dev = &spi->dev;
79788b09229SIker Pedrosa 	struct drm_device *drm;
79888b09229SIker Pedrosa 	int ret;
79988b09229SIker Pedrosa 
80088b09229SIker Pedrosa 	regmap = devm_regmap_init_spi(spi, &st7920_spi_regmap_config);
80188b09229SIker Pedrosa 	if (IS_ERR(regmap))
80288b09229SIker Pedrosa 		return PTR_ERR(regmap);
80388b09229SIker Pedrosa 
80488b09229SIker Pedrosa 	st7920 = devm_drm_dev_alloc(dev, &st7920_drm_driver,
80588b09229SIker Pedrosa 				    struct st7920_device, drm);
80688b09229SIker Pedrosa 	if (IS_ERR(st7920))
80788b09229SIker Pedrosa 		return PTR_ERR(st7920);
80888b09229SIker Pedrosa 
80988b09229SIker Pedrosa 	drm = &st7920->drm;
81088b09229SIker Pedrosa 
81188b09229SIker Pedrosa 	st7920->drm.dev = dev;
81288b09229SIker Pedrosa 	st7920->regmap = regmap;
81388b09229SIker Pedrosa 	st7920->spi = spi;
81488b09229SIker Pedrosa 	st7920->width = ST7920_DEFAULT_WIDTH;
81588b09229SIker Pedrosa 	st7920->height = ST7920_DEFAULT_HEIGHT;
81688b09229SIker Pedrosa 
81788b09229SIker Pedrosa 	st7920->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
81888b09229SIker Pedrosa 	if (IS_ERR(st7920->reset_gpio)) {
81988b09229SIker Pedrosa 		ret = PTR_ERR(st7920->reset_gpio);
82088b09229SIker Pedrosa 		return dev_err_probe(dev, ret, "Unable to retrieve reset GPIO\n");
82188b09229SIker Pedrosa 	}
82288b09229SIker Pedrosa 
82388b09229SIker Pedrosa 	spi_set_drvdata(spi, st7920);
82488b09229SIker Pedrosa 
82588b09229SIker Pedrosa 	ret = st7920_init_modeset(st7920);
82688b09229SIker Pedrosa 	if (ret)
82788b09229SIker Pedrosa 		return ret;
82888b09229SIker Pedrosa 
82988b09229SIker Pedrosa 	ret = drm_dev_register(drm, 0);
83088b09229SIker Pedrosa 	if (ret)
83188b09229SIker Pedrosa 		return dev_err_probe(dev, ret, "DRM device register failed\n");
83288b09229SIker Pedrosa 
83388b09229SIker Pedrosa 	drm_client_setup(drm, NULL);
83488b09229SIker Pedrosa 
83588b09229SIker Pedrosa 	return 0;
83688b09229SIker Pedrosa }
83788b09229SIker Pedrosa 
st7920_remove(struct spi_device * spi)83888b09229SIker Pedrosa static void st7920_remove(struct spi_device *spi)
83988b09229SIker Pedrosa {
84088b09229SIker Pedrosa 	struct st7920_device *st7920 = spi_get_drvdata(spi);
84188b09229SIker Pedrosa 
84288b09229SIker Pedrosa 	drm_dev_unplug(&st7920->drm);
84388b09229SIker Pedrosa 	drm_atomic_helper_shutdown(&st7920->drm);
84488b09229SIker Pedrosa }
84588b09229SIker Pedrosa 
st7920_shutdown(struct spi_device * spi)84688b09229SIker Pedrosa static void st7920_shutdown(struct spi_device *spi)
84788b09229SIker Pedrosa {
84888b09229SIker Pedrosa 	struct st7920_device *st7920 = spi_get_drvdata(spi);
84988b09229SIker Pedrosa 
85088b09229SIker Pedrosa 	drm_atomic_helper_shutdown(&st7920->drm);
85188b09229SIker Pedrosa }
85288b09229SIker Pedrosa 
85388b09229SIker Pedrosa static struct spi_driver st7920_spi_driver = {
85488b09229SIker Pedrosa 	.driver = {
85588b09229SIker Pedrosa 		.name = DRIVER_NAME,
85688b09229SIker Pedrosa 		.of_match_table = st7920_of_match,
85788b09229SIker Pedrosa 	},
85888b09229SIker Pedrosa 	.id_table = st7920_spi_id,
85988b09229SIker Pedrosa 	.probe = st7920_probe,
86088b09229SIker Pedrosa 	.remove = st7920_remove,
86188b09229SIker Pedrosa 	.shutdown = st7920_shutdown,
86288b09229SIker Pedrosa };
86388b09229SIker Pedrosa module_spi_driver(st7920_spi_driver);
86488b09229SIker Pedrosa 
86588b09229SIker Pedrosa MODULE_DESCRIPTION(DRIVER_DESC);
86688b09229SIker Pedrosa MODULE_AUTHOR("Iker Pedrosa <ipedrosam@gmail.com>");
86788b09229SIker Pedrosa MODULE_LICENSE("GPL");
868