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