177b8cabfSNoralf Trønnes // SPDX-License-Identifier: GPL-2.0+
277b8cabfSNoralf Trønnes /*
377b8cabfSNoralf Trønnes * Copyright 2019 Hans de Goede <hdegoede@redhat.com>
477b8cabfSNoralf Trønnes */
577b8cabfSNoralf Trønnes
677b8cabfSNoralf Trønnes #include <linux/module.h>
749eafb20SPaul Cercueil #include <linux/pm.h>
877b8cabfSNoralf Trønnes #include <linux/usb.h>
977b8cabfSNoralf Trønnes
1077b8cabfSNoralf Trønnes #include <drm/drm_atomic_helper.h>
1177b8cabfSNoralf Trønnes #include <drm/drm_atomic_state_helper.h>
1277b8cabfSNoralf Trønnes #include <drm/drm_connector.h>
1377b8cabfSNoralf Trønnes #include <drm/drm_damage_helper.h>
1477b8cabfSNoralf Trønnes #include <drm/drm_drv.h>
15255490f9SVille Syrjälä #include <drm/drm_edid.h>
165bd79b70SThomas Zimmermann #include <drm/drm_fbdev_shmem.h>
1777b8cabfSNoralf Trønnes #include <drm/drm_file.h>
1877b8cabfSNoralf Trønnes #include <drm/drm_format_helper.h>
1977b8cabfSNoralf Trønnes #include <drm/drm_fourcc.h>
20720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
214ac0868dSThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
2277b8cabfSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h>
234ac0868dSThomas Zimmermann #include <drm/drm_gem_shmem_helper.h>
2477b8cabfSNoralf Trønnes #include <drm/drm_ioctl.h>
25b6731025SDaniel Vetter #include <drm/drm_managed.h>
2677b8cabfSNoralf Trønnes #include <drm/drm_modeset_helper_vtables.h>
2777b8cabfSNoralf Trønnes #include <drm/drm_probe_helper.h>
2877b8cabfSNoralf Trønnes #include <drm/drm_simple_kms_helper.h>
2977b8cabfSNoralf Trønnes
3077b8cabfSNoralf Trønnes static bool eco_mode;
3177b8cabfSNoralf Trønnes module_param(eco_mode, bool, 0644);
3277b8cabfSNoralf Trønnes MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)");
3377b8cabfSNoralf Trønnes
3477b8cabfSNoralf Trønnes #define DRIVER_NAME "gm12u320"
3577b8cabfSNoralf Trønnes #define DRIVER_DESC "Grain Media GM12U320 USB projector display"
3677b8cabfSNoralf Trønnes #define DRIVER_DATE "2019"
3777b8cabfSNoralf Trønnes #define DRIVER_MAJOR 1
3877b8cabfSNoralf Trønnes #define DRIVER_MINOR 0
3977b8cabfSNoralf Trønnes
4077b8cabfSNoralf Trønnes /*
4177b8cabfSNoralf Trønnes * The DLP has an actual width of 854 pixels, but that is not a multiple
4277b8cabfSNoralf Trønnes * of 8, breaking things left and right, so we export a width of 848.
4377b8cabfSNoralf Trønnes */
4477b8cabfSNoralf Trønnes #define GM12U320_USER_WIDTH 848
4577b8cabfSNoralf Trønnes #define GM12U320_REAL_WIDTH 854
4677b8cabfSNoralf Trønnes #define GM12U320_HEIGHT 480
4777b8cabfSNoralf Trønnes
4877b8cabfSNoralf Trønnes #define GM12U320_BLOCK_COUNT 20
4977b8cabfSNoralf Trønnes
504abfa2e4SHans de Goede #define GM12U320_ERR(fmt, ...) \
510454bc59SThomas Zimmermann DRM_DEV_ERROR(gm12u320->dev.dev, fmt, ##__VA_ARGS__)
524abfa2e4SHans de Goede
5377b8cabfSNoralf Trønnes #define MISC_RCV_EPT 1
5477b8cabfSNoralf Trønnes #define DATA_RCV_EPT 2
5577b8cabfSNoralf Trønnes #define DATA_SND_EPT 3
5677b8cabfSNoralf Trønnes #define MISC_SND_EPT 4
5777b8cabfSNoralf Trønnes
5877b8cabfSNoralf Trønnes #define DATA_BLOCK_HEADER_SIZE 84
5977b8cabfSNoralf Trønnes #define DATA_BLOCK_CONTENT_SIZE 64512
6077b8cabfSNoralf Trønnes #define DATA_BLOCK_FOOTER_SIZE 20
6177b8cabfSNoralf Trønnes #define DATA_BLOCK_SIZE (DATA_BLOCK_HEADER_SIZE + \
6277b8cabfSNoralf Trønnes DATA_BLOCK_CONTENT_SIZE + \
6377b8cabfSNoralf Trønnes DATA_BLOCK_FOOTER_SIZE)
6477b8cabfSNoralf Trønnes #define DATA_LAST_BLOCK_CONTENT_SIZE 4032
6577b8cabfSNoralf Trønnes #define DATA_LAST_BLOCK_SIZE (DATA_BLOCK_HEADER_SIZE + \
6677b8cabfSNoralf Trønnes DATA_LAST_BLOCK_CONTENT_SIZE + \
6777b8cabfSNoralf Trønnes DATA_BLOCK_FOOTER_SIZE)
6877b8cabfSNoralf Trønnes
6977b8cabfSNoralf Trønnes #define CMD_SIZE 31
7077b8cabfSNoralf Trønnes #define READ_STATUS_SIZE 13
7177b8cabfSNoralf Trønnes #define MISC_VALUE_SIZE 4
7277b8cabfSNoralf Trønnes
737583028dSJinjie Ruan #define CMD_TIMEOUT 200
747583028dSJinjie Ruan #define DATA_TIMEOUT 1000
757583028dSJinjie Ruan #define IDLE_TIMEOUT 2000
767583028dSJinjie Ruan #define FIRST_FRAME_TIMEOUT 2000
7777b8cabfSNoralf Trønnes
7877b8cabfSNoralf Trønnes #define MISC_REQ_GET_SET_ECO_A 0xff
7977b8cabfSNoralf Trønnes #define MISC_REQ_GET_SET_ECO_B 0x35
8077b8cabfSNoralf Trønnes /* Windows driver does once every second, with arg d = 1, other args 0 */
8177b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN1_A 0xff
8277b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN1_B 0x38
8377b8cabfSNoralf Trønnes /* Windows driver does this on init, with arg a, b = 0, c = 0xa0, d = 4 */
8477b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN2_A 0xa5
8577b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN2_B 0x00
8677b8cabfSNoralf Trønnes
8777b8cabfSNoralf Trønnes struct gm12u320_device {
8877b8cabfSNoralf Trønnes struct drm_device dev;
89659ab7a4SThomas Zimmermann struct device *dmadev;
9077b8cabfSNoralf Trønnes struct drm_simple_display_pipe pipe;
9177b8cabfSNoralf Trønnes struct drm_connector conn;
9277b8cabfSNoralf Trønnes unsigned char *cmd_buf;
9377b8cabfSNoralf Trønnes unsigned char *data_buf[GM12U320_BLOCK_COUNT];
9477b8cabfSNoralf Trønnes struct {
958f2cb937SDaniel Vetter struct delayed_work work;
9677b8cabfSNoralf Trønnes struct mutex lock;
9777b8cabfSNoralf Trønnes struct drm_framebuffer *fb;
9877b8cabfSNoralf Trønnes struct drm_rect rect;
998f2cb937SDaniel Vetter int frame;
1008f2cb937SDaniel Vetter int draw_status_timeout;
1017938f421SLucas De Marchi struct iosys_map src_map;
10277b8cabfSNoralf Trønnes } fb_update;
10377b8cabfSNoralf Trønnes };
10477b8cabfSNoralf Trønnes
1057ced4801SDaniel Vetter #define to_gm12u320(__dev) container_of(__dev, struct gm12u320_device, dev)
1067ced4801SDaniel Vetter
10777b8cabfSNoralf Trønnes static const char cmd_data[CMD_SIZE] = {
10877b8cabfSNoralf Trønnes 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
10977b8cabfSNoralf Trønnes 0x68, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff,
11077b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x80, 0x00,
11177b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
11277b8cabfSNoralf Trønnes };
11377b8cabfSNoralf Trønnes
11477b8cabfSNoralf Trønnes static const char cmd_draw[CMD_SIZE] = {
11577b8cabfSNoralf Trønnes 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
11677b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe,
11777b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0xc0, 0xd1, 0x05, 0x00, 0x40,
11877b8cabfSNoralf Trønnes 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
11977b8cabfSNoralf Trønnes };
12077b8cabfSNoralf Trønnes
12177b8cabfSNoralf Trønnes static const char cmd_misc[CMD_SIZE] = {
12277b8cabfSNoralf Trønnes 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00,
12377b8cabfSNoralf Trønnes 0x04, 0x00, 0x00, 0x00, 0x80, 0x01, 0x10, 0xfd,
12477b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00,
12577b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
12677b8cabfSNoralf Trønnes };
12777b8cabfSNoralf Trønnes
12877b8cabfSNoralf Trønnes static const char data_block_header[DATA_BLOCK_HEADER_SIZE] = {
12977b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13077b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13177b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13277b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13377b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13477b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13577b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13677b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13777b8cabfSNoralf Trønnes 0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
13877b8cabfSNoralf Trønnes 0x00, 0x04, 0x15, 0x00, 0x00, 0xfc, 0x00, 0x00,
13977b8cabfSNoralf Trønnes 0x01, 0x00, 0x00, 0xdb
14077b8cabfSNoralf Trønnes };
14177b8cabfSNoralf Trønnes
14277b8cabfSNoralf Trønnes static const char data_last_block_header[DATA_BLOCK_HEADER_SIZE] = {
14377b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14477b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14577b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14677b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14777b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14877b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14977b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15077b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15177b8cabfSNoralf Trønnes 0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15277b8cabfSNoralf Trønnes 0x2a, 0x00, 0x20, 0x00, 0xc0, 0x0f, 0x00, 0x00,
15377b8cabfSNoralf Trønnes 0x01, 0x00, 0x00, 0xd7
15477b8cabfSNoralf Trønnes };
15577b8cabfSNoralf Trønnes
15677b8cabfSNoralf Trønnes static const char data_block_footer[DATA_BLOCK_FOOTER_SIZE] = {
15777b8cabfSNoralf Trønnes 0xfb, 0x14, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00,
15877b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15977b8cabfSNoralf Trønnes 0x80, 0x00, 0x00, 0x4f
16077b8cabfSNoralf Trønnes };
16177b8cabfSNoralf Trønnes
gm12u320_to_usb_device(struct gm12u320_device * gm12u320)1620454bc59SThomas Zimmermann static inline struct usb_device *gm12u320_to_usb_device(struct gm12u320_device *gm12u320)
1630454bc59SThomas Zimmermann {
1640454bc59SThomas Zimmermann return interface_to_usbdev(to_usb_interface(gm12u320->dev.dev));
1650454bc59SThomas Zimmermann }
1660454bc59SThomas Zimmermann
gm12u320_usb_alloc(struct gm12u320_device * gm12u320)16777b8cabfSNoralf Trønnes static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320)
16877b8cabfSNoralf Trønnes {
16977b8cabfSNoralf Trønnes int i, block_size;
17077b8cabfSNoralf Trønnes const char *hdr;
17177b8cabfSNoralf Trønnes
17208373edcSDaniel Vetter gm12u320->cmd_buf = drmm_kmalloc(&gm12u320->dev, CMD_SIZE, GFP_KERNEL);
17377b8cabfSNoralf Trønnes if (!gm12u320->cmd_buf)
17477b8cabfSNoralf Trønnes return -ENOMEM;
17577b8cabfSNoralf Trønnes
17677b8cabfSNoralf Trønnes for (i = 0; i < GM12U320_BLOCK_COUNT; i++) {
17777b8cabfSNoralf Trønnes if (i == GM12U320_BLOCK_COUNT - 1) {
17877b8cabfSNoralf Trønnes block_size = DATA_LAST_BLOCK_SIZE;
17977b8cabfSNoralf Trønnes hdr = data_last_block_header;
18077b8cabfSNoralf Trønnes } else {
18177b8cabfSNoralf Trønnes block_size = DATA_BLOCK_SIZE;
18277b8cabfSNoralf Trønnes hdr = data_block_header;
18377b8cabfSNoralf Trønnes }
18477b8cabfSNoralf Trønnes
18508373edcSDaniel Vetter gm12u320->data_buf[i] = drmm_kzalloc(&gm12u320->dev,
18608373edcSDaniel Vetter block_size, GFP_KERNEL);
18777b8cabfSNoralf Trønnes if (!gm12u320->data_buf[i])
18877b8cabfSNoralf Trønnes return -ENOMEM;
18977b8cabfSNoralf Trønnes
19077b8cabfSNoralf Trønnes memcpy(gm12u320->data_buf[i], hdr, DATA_BLOCK_HEADER_SIZE);
19177b8cabfSNoralf Trønnes memcpy(gm12u320->data_buf[i] +
19277b8cabfSNoralf Trønnes (block_size - DATA_BLOCK_FOOTER_SIZE),
19377b8cabfSNoralf Trønnes data_block_footer, DATA_BLOCK_FOOTER_SIZE);
19477b8cabfSNoralf Trønnes }
19577b8cabfSNoralf Trønnes
19677b8cabfSNoralf Trønnes return 0;
19777b8cabfSNoralf Trønnes }
19877b8cabfSNoralf Trønnes
gm12u320_misc_request(struct gm12u320_device * gm12u320,u8 req_a,u8 req_b,u8 arg_a,u8 arg_b,u8 arg_c,u8 arg_d)19977b8cabfSNoralf Trønnes static int gm12u320_misc_request(struct gm12u320_device *gm12u320,
20077b8cabfSNoralf Trønnes u8 req_a, u8 req_b,
20177b8cabfSNoralf Trønnes u8 arg_a, u8 arg_b, u8 arg_c, u8 arg_d)
20277b8cabfSNoralf Trønnes {
2030454bc59SThomas Zimmermann struct usb_device *udev = gm12u320_to_usb_device(gm12u320);
20477b8cabfSNoralf Trønnes int ret, len;
20577b8cabfSNoralf Trønnes
20677b8cabfSNoralf Trønnes memcpy(gm12u320->cmd_buf, &cmd_misc, CMD_SIZE);
20777b8cabfSNoralf Trønnes gm12u320->cmd_buf[20] = req_a;
20877b8cabfSNoralf Trønnes gm12u320->cmd_buf[21] = req_b;
20977b8cabfSNoralf Trønnes gm12u320->cmd_buf[22] = arg_a;
21077b8cabfSNoralf Trønnes gm12u320->cmd_buf[23] = arg_b;
21177b8cabfSNoralf Trønnes gm12u320->cmd_buf[24] = arg_c;
21277b8cabfSNoralf Trønnes gm12u320->cmd_buf[25] = arg_d;
21377b8cabfSNoralf Trønnes
21477b8cabfSNoralf Trønnes /* Send request */
2150454bc59SThomas Zimmermann ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, MISC_SND_EPT),
21677b8cabfSNoralf Trønnes gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
21777b8cabfSNoralf Trønnes if (ret || len != CMD_SIZE) {
2184abfa2e4SHans de Goede GM12U320_ERR("Misc. req. error %d\n", ret);
21977b8cabfSNoralf Trønnes return -EIO;
22077b8cabfSNoralf Trønnes }
22177b8cabfSNoralf Trønnes
22277b8cabfSNoralf Trønnes /* Read value */
2230454bc59SThomas Zimmermann ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, MISC_RCV_EPT),
22477b8cabfSNoralf Trønnes gm12u320->cmd_buf, MISC_VALUE_SIZE, &len,
22577b8cabfSNoralf Trønnes DATA_TIMEOUT);
22677b8cabfSNoralf Trønnes if (ret || len != MISC_VALUE_SIZE) {
2274abfa2e4SHans de Goede GM12U320_ERR("Misc. value error %d\n", ret);
22877b8cabfSNoralf Trønnes return -EIO;
22977b8cabfSNoralf Trønnes }
23077b8cabfSNoralf Trønnes /* cmd_buf[0] now contains the read value, which we don't use */
23177b8cabfSNoralf Trønnes
23277b8cabfSNoralf Trønnes /* Read status */
2330454bc59SThomas Zimmermann ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, MISC_RCV_EPT),
23477b8cabfSNoralf Trønnes gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
23577b8cabfSNoralf Trønnes CMD_TIMEOUT);
23677b8cabfSNoralf Trønnes if (ret || len != READ_STATUS_SIZE) {
2374abfa2e4SHans de Goede GM12U320_ERR("Misc. status error %d\n", ret);
23877b8cabfSNoralf Trønnes return -EIO;
23977b8cabfSNoralf Trønnes }
24077b8cabfSNoralf Trønnes
24177b8cabfSNoralf Trønnes return 0;
24277b8cabfSNoralf Trønnes }
24377b8cabfSNoralf Trønnes
gm12u320_32bpp_to_24bpp_packed(u8 * dst,u8 * src,int len)24477b8cabfSNoralf Trønnes static void gm12u320_32bpp_to_24bpp_packed(u8 *dst, u8 *src, int len)
24577b8cabfSNoralf Trønnes {
24677b8cabfSNoralf Trønnes while (len--) {
24777b8cabfSNoralf Trønnes *dst++ = *src++;
24877b8cabfSNoralf Trønnes *dst++ = *src++;
24977b8cabfSNoralf Trønnes *dst++ = *src++;
25077b8cabfSNoralf Trønnes src++;
25177b8cabfSNoralf Trønnes }
25277b8cabfSNoralf Trønnes }
25377b8cabfSNoralf Trønnes
gm12u320_copy_fb_to_blocks(struct gm12u320_device * gm12u320)25477b8cabfSNoralf Trønnes static void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320)
25577b8cabfSNoralf Trønnes {
25677b8cabfSNoralf Trønnes int block, dst_offset, len, remain, ret, x1, x2, y1, y2;
25777b8cabfSNoralf Trønnes struct drm_framebuffer *fb;
25877b8cabfSNoralf Trønnes void *vaddr;
25977b8cabfSNoralf Trønnes u8 *src;
26077b8cabfSNoralf Trønnes
26177b8cabfSNoralf Trønnes mutex_lock(&gm12u320->fb_update.lock);
26277b8cabfSNoralf Trønnes
26377b8cabfSNoralf Trønnes if (!gm12u320->fb_update.fb)
26477b8cabfSNoralf Trønnes goto unlock;
26577b8cabfSNoralf Trønnes
26677b8cabfSNoralf Trønnes fb = gm12u320->fb_update.fb;
26777b8cabfSNoralf Trønnes x1 = gm12u320->fb_update.rect.x1;
26877b8cabfSNoralf Trønnes x2 = gm12u320->fb_update.rect.x2;
26977b8cabfSNoralf Trønnes y1 = gm12u320->fb_update.rect.y1;
27077b8cabfSNoralf Trønnes y2 = gm12u320->fb_update.rect.y2;
2714ac0868dSThomas Zimmermann vaddr = gm12u320->fb_update.src_map.vaddr; /* TODO: Use mapping abstraction properly */
27277b8cabfSNoralf Trønnes
273329e2c42SThomas Zimmermann ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
27477b8cabfSNoralf Trønnes if (ret) {
275329e2c42SThomas Zimmermann GM12U320_ERR("drm_gem_fb_begin_cpu_access err: %d\n", ret);
2764ac0868dSThomas Zimmermann goto put_fb;
27777b8cabfSNoralf Trønnes }
27877b8cabfSNoralf Trønnes
27977b8cabfSNoralf Trønnes src = vaddr + y1 * fb->pitches[0] + x1 * 4;
28077b8cabfSNoralf Trønnes
28177b8cabfSNoralf Trønnes x1 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
28277b8cabfSNoralf Trønnes x2 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2;
28377b8cabfSNoralf Trønnes
28477b8cabfSNoralf Trønnes for (; y1 < y2; y1++) {
28577b8cabfSNoralf Trønnes remain = 0;
28677b8cabfSNoralf Trønnes len = (x2 - x1) * 3;
28777b8cabfSNoralf Trønnes dst_offset = (y1 * GM12U320_REAL_WIDTH + x1) * 3;
28877b8cabfSNoralf Trønnes block = dst_offset / DATA_BLOCK_CONTENT_SIZE;
28977b8cabfSNoralf Trønnes dst_offset %= DATA_BLOCK_CONTENT_SIZE;
29077b8cabfSNoralf Trønnes
29177b8cabfSNoralf Trønnes if ((dst_offset + len) > DATA_BLOCK_CONTENT_SIZE) {
29277b8cabfSNoralf Trønnes remain = dst_offset + len - DATA_BLOCK_CONTENT_SIZE;
29377b8cabfSNoralf Trønnes len = DATA_BLOCK_CONTENT_SIZE - dst_offset;
29477b8cabfSNoralf Trønnes }
29577b8cabfSNoralf Trønnes
29677b8cabfSNoralf Trønnes dst_offset += DATA_BLOCK_HEADER_SIZE;
29777b8cabfSNoralf Trønnes len /= 3;
29877b8cabfSNoralf Trønnes
29977b8cabfSNoralf Trønnes gm12u320_32bpp_to_24bpp_packed(
30077b8cabfSNoralf Trønnes gm12u320->data_buf[block] + dst_offset,
30177b8cabfSNoralf Trønnes src, len);
30277b8cabfSNoralf Trønnes
30377b8cabfSNoralf Trønnes if (remain) {
30477b8cabfSNoralf Trønnes block++;
30577b8cabfSNoralf Trønnes dst_offset = DATA_BLOCK_HEADER_SIZE;
30677b8cabfSNoralf Trønnes gm12u320_32bpp_to_24bpp_packed(
30777b8cabfSNoralf Trønnes gm12u320->data_buf[block] + dst_offset,
30877b8cabfSNoralf Trønnes src + len * 4, remain / 3);
30977b8cabfSNoralf Trønnes }
31077b8cabfSNoralf Trønnes src += fb->pitches[0];
31177b8cabfSNoralf Trønnes }
31277b8cabfSNoralf Trønnes
313329e2c42SThomas Zimmermann drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
31477b8cabfSNoralf Trønnes put_fb:
31577b8cabfSNoralf Trønnes drm_framebuffer_put(fb);
31677b8cabfSNoralf Trønnes gm12u320->fb_update.fb = NULL;
31777b8cabfSNoralf Trønnes unlock:
31877b8cabfSNoralf Trønnes mutex_unlock(&gm12u320->fb_update.lock);
31977b8cabfSNoralf Trønnes }
32077b8cabfSNoralf Trønnes
gm12u320_fb_update_work(struct work_struct * work)32177b8cabfSNoralf Trønnes static void gm12u320_fb_update_work(struct work_struct *work)
32277b8cabfSNoralf Trønnes {
32377b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320 =
3248f2cb937SDaniel Vetter container_of(to_delayed_work(work), struct gm12u320_device,
3258f2cb937SDaniel Vetter fb_update.work);
3260454bc59SThomas Zimmermann struct usb_device *udev = gm12u320_to_usb_device(gm12u320);
32777b8cabfSNoralf Trønnes int block, block_size, len;
32877b8cabfSNoralf Trønnes int ret = 0;
32977b8cabfSNoralf Trønnes
33077b8cabfSNoralf Trønnes gm12u320_copy_fb_to_blocks(gm12u320);
33177b8cabfSNoralf Trønnes
33277b8cabfSNoralf Trønnes for (block = 0; block < GM12U320_BLOCK_COUNT; block++) {
33377b8cabfSNoralf Trønnes if (block == GM12U320_BLOCK_COUNT - 1)
33477b8cabfSNoralf Trønnes block_size = DATA_LAST_BLOCK_SIZE;
33577b8cabfSNoralf Trønnes else
33677b8cabfSNoralf Trønnes block_size = DATA_BLOCK_SIZE;
33777b8cabfSNoralf Trønnes
33877b8cabfSNoralf Trønnes /* Send data command to device */
33977b8cabfSNoralf Trønnes memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE);
34077b8cabfSNoralf Trønnes gm12u320->cmd_buf[8] = block_size & 0xff;
34177b8cabfSNoralf Trønnes gm12u320->cmd_buf[9] = block_size >> 8;
34277b8cabfSNoralf Trønnes gm12u320->cmd_buf[20] = 0xfc - block * 4;
3438f2cb937SDaniel Vetter gm12u320->cmd_buf[21] =
3448f2cb937SDaniel Vetter block | (gm12u320->fb_update.frame << 7);
34577b8cabfSNoralf Trønnes
3460454bc59SThomas Zimmermann ret = usb_bulk_msg(udev,
3470454bc59SThomas Zimmermann usb_sndbulkpipe(udev, DATA_SND_EPT),
34877b8cabfSNoralf Trønnes gm12u320->cmd_buf, CMD_SIZE, &len,
34977b8cabfSNoralf Trønnes CMD_TIMEOUT);
35077b8cabfSNoralf Trønnes if (ret || len != CMD_SIZE)
35177b8cabfSNoralf Trønnes goto err;
35277b8cabfSNoralf Trønnes
35377b8cabfSNoralf Trønnes /* Send data block to device */
3540454bc59SThomas Zimmermann ret = usb_bulk_msg(udev,
3550454bc59SThomas Zimmermann usb_sndbulkpipe(udev, DATA_SND_EPT),
35677b8cabfSNoralf Trønnes gm12u320->data_buf[block], block_size,
35777b8cabfSNoralf Trønnes &len, DATA_TIMEOUT);
35877b8cabfSNoralf Trønnes if (ret || len != block_size)
35977b8cabfSNoralf Trønnes goto err;
36077b8cabfSNoralf Trønnes
36177b8cabfSNoralf Trønnes /* Read status */
3620454bc59SThomas Zimmermann ret = usb_bulk_msg(udev,
3630454bc59SThomas Zimmermann usb_rcvbulkpipe(udev, DATA_RCV_EPT),
36477b8cabfSNoralf Trønnes gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
36577b8cabfSNoralf Trønnes CMD_TIMEOUT);
36677b8cabfSNoralf Trønnes if (ret || len != READ_STATUS_SIZE)
36777b8cabfSNoralf Trønnes goto err;
36877b8cabfSNoralf Trønnes }
36977b8cabfSNoralf Trønnes
37077b8cabfSNoralf Trønnes /* Send draw command to device */
37177b8cabfSNoralf Trønnes memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE);
3720454bc59SThomas Zimmermann ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, DATA_SND_EPT),
37377b8cabfSNoralf Trønnes gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT);
37477b8cabfSNoralf Trønnes if (ret || len != CMD_SIZE)
37577b8cabfSNoralf Trønnes goto err;
37677b8cabfSNoralf Trønnes
37777b8cabfSNoralf Trønnes /* Read status */
3780454bc59SThomas Zimmermann ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, DATA_RCV_EPT),
37977b8cabfSNoralf Trønnes gm12u320->cmd_buf, READ_STATUS_SIZE, &len,
3808f2cb937SDaniel Vetter gm12u320->fb_update.draw_status_timeout);
38177b8cabfSNoralf Trønnes if (ret || len != READ_STATUS_SIZE)
38277b8cabfSNoralf Trønnes goto err;
38377b8cabfSNoralf Trønnes
3848f2cb937SDaniel Vetter gm12u320->fb_update.draw_status_timeout = CMD_TIMEOUT;
3858f2cb937SDaniel Vetter gm12u320->fb_update.frame = !gm12u320->fb_update.frame;
38677b8cabfSNoralf Trønnes
38777b8cabfSNoralf Trønnes /*
38877b8cabfSNoralf Trønnes * We must draw a frame every 2s otherwise the projector
38977b8cabfSNoralf Trønnes * switches back to showing its logo.
39077b8cabfSNoralf Trønnes */
3918f2cb937SDaniel Vetter queue_delayed_work(system_long_wq, &gm12u320->fb_update.work,
3927583028dSJinjie Ruan msecs_to_jiffies(IDLE_TIMEOUT));
3938f2cb937SDaniel Vetter
39477b8cabfSNoralf Trønnes return;
39577b8cabfSNoralf Trønnes err:
39677b8cabfSNoralf Trønnes /* Do not log errors caused by module unload or device unplug */
397ac9fd659SHans de Goede if (ret != -ENODEV && ret != -ECONNRESET && ret != -ESHUTDOWN)
3984abfa2e4SHans de Goede GM12U320_ERR("Frame update error: %d\n", ret);
39977b8cabfSNoralf Trønnes }
40077b8cabfSNoralf Trønnes
gm12u320_fb_mark_dirty(struct drm_framebuffer * fb,const struct iosys_map * map,struct drm_rect * dirty)4017938f421SLucas De Marchi static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb,
4027938f421SLucas De Marchi const struct iosys_map *map,
40377b8cabfSNoralf Trønnes struct drm_rect *dirty)
40477b8cabfSNoralf Trønnes {
4057ced4801SDaniel Vetter struct gm12u320_device *gm12u320 = to_gm12u320(fb->dev);
40677b8cabfSNoralf Trønnes struct drm_framebuffer *old_fb = NULL;
40777b8cabfSNoralf Trønnes bool wakeup = false;
40877b8cabfSNoralf Trønnes
40977b8cabfSNoralf Trønnes mutex_lock(&gm12u320->fb_update.lock);
41077b8cabfSNoralf Trønnes
41177b8cabfSNoralf Trønnes if (gm12u320->fb_update.fb != fb) {
41277b8cabfSNoralf Trønnes old_fb = gm12u320->fb_update.fb;
41377b8cabfSNoralf Trønnes drm_framebuffer_get(fb);
41477b8cabfSNoralf Trønnes gm12u320->fb_update.fb = fb;
41577b8cabfSNoralf Trønnes gm12u320->fb_update.rect = *dirty;
4164ac0868dSThomas Zimmermann gm12u320->fb_update.src_map = *map;
41777b8cabfSNoralf Trønnes wakeup = true;
41877b8cabfSNoralf Trønnes } else {
41977b8cabfSNoralf Trønnes struct drm_rect *rect = &gm12u320->fb_update.rect;
42077b8cabfSNoralf Trønnes
42177b8cabfSNoralf Trønnes rect->x1 = min(rect->x1, dirty->x1);
42277b8cabfSNoralf Trønnes rect->y1 = min(rect->y1, dirty->y1);
42377b8cabfSNoralf Trønnes rect->x2 = max(rect->x2, dirty->x2);
42477b8cabfSNoralf Trønnes rect->y2 = max(rect->y2, dirty->y2);
42577b8cabfSNoralf Trønnes }
42677b8cabfSNoralf Trønnes
42777b8cabfSNoralf Trønnes mutex_unlock(&gm12u320->fb_update.lock);
42877b8cabfSNoralf Trønnes
42977b8cabfSNoralf Trønnes if (wakeup)
4308f2cb937SDaniel Vetter mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0);
43177b8cabfSNoralf Trønnes
43277b8cabfSNoralf Trønnes if (old_fb)
43377b8cabfSNoralf Trønnes drm_framebuffer_put(old_fb);
43477b8cabfSNoralf Trønnes }
43577b8cabfSNoralf Trønnes
gm12u320_stop_fb_update(struct gm12u320_device * gm12u320)43677b8cabfSNoralf Trønnes static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320)
43777b8cabfSNoralf Trønnes {
4388f2cb937SDaniel Vetter struct drm_framebuffer *old_fb;
43977b8cabfSNoralf Trønnes
4408f2cb937SDaniel Vetter cancel_delayed_work_sync(&gm12u320->fb_update.work);
44177b8cabfSNoralf Trønnes
44277b8cabfSNoralf Trønnes mutex_lock(&gm12u320->fb_update.lock);
4438f2cb937SDaniel Vetter old_fb = gm12u320->fb_update.fb;
44477b8cabfSNoralf Trønnes gm12u320->fb_update.fb = NULL;
4457938f421SLucas De Marchi iosys_map_clear(&gm12u320->fb_update.src_map);
44677b8cabfSNoralf Trønnes mutex_unlock(&gm12u320->fb_update.lock);
4478f2cb937SDaniel Vetter
4488f2cb937SDaniel Vetter drm_framebuffer_put(old_fb);
44977b8cabfSNoralf Trønnes }
45077b8cabfSNoralf Trønnes
gm12u320_set_ecomode(struct gm12u320_device * gm12u320)45177b8cabfSNoralf Trønnes static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320)
45277b8cabfSNoralf Trønnes {
45377b8cabfSNoralf Trønnes return gm12u320_misc_request(gm12u320, MISC_REQ_GET_SET_ECO_A,
45477b8cabfSNoralf Trønnes MISC_REQ_GET_SET_ECO_B, 0x01 /* set */,
45577b8cabfSNoralf Trønnes eco_mode ? 0x01 : 0x00, 0x00, 0x01);
45677b8cabfSNoralf Trønnes }
45777b8cabfSNoralf Trønnes
45877b8cabfSNoralf Trønnes /* ------------------------------------------------------------------ */
45977b8cabfSNoralf Trønnes /* gm12u320 connector */
46077b8cabfSNoralf Trønnes
46177b8cabfSNoralf Trønnes /*
46277b8cabfSNoralf Trønnes * We use fake EDID info so that userspace know that it is dealing with
46377b8cabfSNoralf Trønnes * an Acer projector, rather then listing this as an "unknown" monitor.
46477b8cabfSNoralf Trønnes * Note this assumes this driver is only ever used with the Acer C120, if we
46577b8cabfSNoralf Trønnes * add support for other devices the vendor and model should be parameterized.
46677b8cabfSNoralf Trønnes */
467*84addde4SJani Nikula static const struct edid gm12u320_edid = {
46877b8cabfSNoralf Trønnes .header = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 },
46977b8cabfSNoralf Trønnes .mfg_id = { 0x04, 0x72 }, /* "ACR" */
47077b8cabfSNoralf Trønnes .prod_code = { 0x20, 0xc1 }, /* C120h */
47177b8cabfSNoralf Trønnes .serial = 0xaa55aa55,
47277b8cabfSNoralf Trønnes .mfg_week = 1,
47377b8cabfSNoralf Trønnes .mfg_year = 16,
47477b8cabfSNoralf Trønnes .version = 1, /* EDID 1.3 */
47577b8cabfSNoralf Trønnes .revision = 3, /* EDID 1.3 */
47677b8cabfSNoralf Trønnes .input = 0x08, /* Analog input */
47777b8cabfSNoralf Trønnes .features = 0x0a, /* Pref timing in DTD 1 */
47877b8cabfSNoralf Trønnes .standard_timings = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 },
47977b8cabfSNoralf Trønnes { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } },
48077b8cabfSNoralf Trønnes .detailed_timings = { {
48177b8cabfSNoralf Trønnes .pixel_clock = 3383,
48277b8cabfSNoralf Trønnes /* hactive = 848, hblank = 256 */
48377b8cabfSNoralf Trønnes .data.pixel_data.hactive_lo = 0x50,
48477b8cabfSNoralf Trønnes .data.pixel_data.hblank_lo = 0x00,
48577b8cabfSNoralf Trønnes .data.pixel_data.hactive_hblank_hi = 0x31,
48677b8cabfSNoralf Trønnes /* vactive = 480, vblank = 28 */
48777b8cabfSNoralf Trønnes .data.pixel_data.vactive_lo = 0xe0,
48877b8cabfSNoralf Trønnes .data.pixel_data.vblank_lo = 0x1c,
48977b8cabfSNoralf Trønnes .data.pixel_data.vactive_vblank_hi = 0x10,
49077b8cabfSNoralf Trønnes /* hsync offset 40 pw 128, vsync offset 1 pw 4 */
49177b8cabfSNoralf Trønnes .data.pixel_data.hsync_offset_lo = 0x28,
49277b8cabfSNoralf Trønnes .data.pixel_data.hsync_pulse_width_lo = 0x80,
49377b8cabfSNoralf Trønnes .data.pixel_data.vsync_offset_pulse_width_lo = 0x14,
49477b8cabfSNoralf Trønnes .data.pixel_data.hsync_vsync_offset_pulse_width_hi = 0x00,
49577b8cabfSNoralf Trønnes /* Digital separate syncs, hsync+, vsync+ */
49677b8cabfSNoralf Trønnes .data.pixel_data.misc = 0x1e,
49777b8cabfSNoralf Trønnes }, {
49877b8cabfSNoralf Trønnes .pixel_clock = 0,
49977b8cabfSNoralf Trønnes .data.other_data.type = 0xfd, /* Monitor ranges */
50077b8cabfSNoralf Trønnes .data.other_data.data.range.min_vfreq = 59,
50177b8cabfSNoralf Trønnes .data.other_data.data.range.max_vfreq = 61,
50277b8cabfSNoralf Trønnes .data.other_data.data.range.min_hfreq_khz = 29,
50377b8cabfSNoralf Trønnes .data.other_data.data.range.max_hfreq_khz = 32,
50477b8cabfSNoralf Trønnes .data.other_data.data.range.pixel_clock_mhz = 4, /* 40 MHz */
50577b8cabfSNoralf Trønnes .data.other_data.data.range.flags = 0,
50677b8cabfSNoralf Trønnes .data.other_data.data.range.formula.cvt = {
50777b8cabfSNoralf Trønnes 0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 },
50877b8cabfSNoralf Trønnes }, {
50977b8cabfSNoralf Trønnes .pixel_clock = 0,
51077b8cabfSNoralf Trønnes .data.other_data.type = 0xfc, /* Model string */
51177b8cabfSNoralf Trønnes .data.other_data.data.str.str = {
51277b8cabfSNoralf Trønnes 'P', 'r', 'o', 'j', 'e', 'c', 't', 'o', 'r', '\n',
51377b8cabfSNoralf Trønnes ' ', ' ', ' ' },
51477b8cabfSNoralf Trønnes }, {
51577b8cabfSNoralf Trønnes .pixel_clock = 0,
51677b8cabfSNoralf Trønnes .data.other_data.type = 0xfe, /* Unspecified text / padding */
51777b8cabfSNoralf Trønnes .data.other_data.data.str.str = {
51877b8cabfSNoralf Trønnes '\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
51977b8cabfSNoralf Trønnes ' ', ' ', ' ' },
52077b8cabfSNoralf Trønnes } },
52177b8cabfSNoralf Trønnes .checksum = 0x13,
52277b8cabfSNoralf Trønnes };
52377b8cabfSNoralf Trønnes
gm12u320_conn_get_modes(struct drm_connector * connector)52477b8cabfSNoralf Trønnes static int gm12u320_conn_get_modes(struct drm_connector *connector)
52577b8cabfSNoralf Trønnes {
526*84addde4SJani Nikula const struct drm_edid *drm_edid;
527*84addde4SJani Nikula int count;
528*84addde4SJani Nikula
529*84addde4SJani Nikula drm_edid = drm_edid_alloc(&gm12u320_edid, sizeof(gm12u320_edid));
530*84addde4SJani Nikula drm_edid_connector_update(connector, drm_edid);
531*84addde4SJani Nikula count = drm_edid_connector_add_modes(connector);
532*84addde4SJani Nikula drm_edid_free(drm_edid);
533*84addde4SJani Nikula
534*84addde4SJani Nikula return count;
53577b8cabfSNoralf Trønnes }
53677b8cabfSNoralf Trønnes
53777b8cabfSNoralf Trønnes static const struct drm_connector_helper_funcs gm12u320_conn_helper_funcs = {
53877b8cabfSNoralf Trønnes .get_modes = gm12u320_conn_get_modes,
53977b8cabfSNoralf Trønnes };
54077b8cabfSNoralf Trønnes
54177b8cabfSNoralf Trønnes static const struct drm_connector_funcs gm12u320_conn_funcs = {
54277b8cabfSNoralf Trønnes .fill_modes = drm_helper_probe_single_connector_modes,
54377b8cabfSNoralf Trønnes .destroy = drm_connector_cleanup,
54477b8cabfSNoralf Trønnes .reset = drm_atomic_helper_connector_reset,
54577b8cabfSNoralf Trønnes .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
54677b8cabfSNoralf Trønnes .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
54777b8cabfSNoralf Trønnes };
54877b8cabfSNoralf Trønnes
gm12u320_conn_init(struct gm12u320_device * gm12u320)54977b8cabfSNoralf Trønnes static int gm12u320_conn_init(struct gm12u320_device *gm12u320)
55077b8cabfSNoralf Trønnes {
55177b8cabfSNoralf Trønnes drm_connector_helper_add(&gm12u320->conn, &gm12u320_conn_helper_funcs);
55277b8cabfSNoralf Trønnes return drm_connector_init(&gm12u320->dev, &gm12u320->conn,
55377b8cabfSNoralf Trønnes &gm12u320_conn_funcs, DRM_MODE_CONNECTOR_VGA);
55477b8cabfSNoralf Trønnes }
55577b8cabfSNoralf Trønnes
55677b8cabfSNoralf Trønnes /* ------------------------------------------------------------------ */
55777b8cabfSNoralf Trønnes /* gm12u320 (simple) display pipe */
55877b8cabfSNoralf Trønnes
gm12u320_pipe_enable(struct drm_simple_display_pipe * pipe,struct drm_crtc_state * crtc_state,struct drm_plane_state * plane_state)55977b8cabfSNoralf Trønnes static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe,
56077b8cabfSNoralf Trønnes struct drm_crtc_state *crtc_state,
56177b8cabfSNoralf Trønnes struct drm_plane_state *plane_state)
56277b8cabfSNoralf Trønnes {
56377b8cabfSNoralf Trønnes struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT };
5647ced4801SDaniel Vetter struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
5654ac0868dSThomas Zimmermann struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
56677b8cabfSNoralf Trønnes
5678f2cb937SDaniel Vetter gm12u320->fb_update.draw_status_timeout = FIRST_FRAME_TIMEOUT;
568229d9468SThomas Zimmermann gm12u320_fb_mark_dirty(plane_state->fb, &shadow_plane_state->data[0], &rect);
56977b8cabfSNoralf Trønnes }
57077b8cabfSNoralf Trønnes
gm12u320_pipe_disable(struct drm_simple_display_pipe * pipe)57177b8cabfSNoralf Trønnes static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe)
57277b8cabfSNoralf Trønnes {
5737ced4801SDaniel Vetter struct gm12u320_device *gm12u320 = to_gm12u320(pipe->crtc.dev);
57477b8cabfSNoralf Trønnes
57577b8cabfSNoralf Trønnes gm12u320_stop_fb_update(gm12u320);
57677b8cabfSNoralf Trønnes }
57777b8cabfSNoralf Trønnes
gm12u320_pipe_update(struct drm_simple_display_pipe * pipe,struct drm_plane_state * old_state)57877b8cabfSNoralf Trønnes static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
57977b8cabfSNoralf Trønnes struct drm_plane_state *old_state)
58077b8cabfSNoralf Trønnes {
58177b8cabfSNoralf Trønnes struct drm_plane_state *state = pipe->plane.state;
5824ac0868dSThomas Zimmermann struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
58377b8cabfSNoralf Trønnes struct drm_rect rect;
58477b8cabfSNoralf Trønnes
58577b8cabfSNoralf Trønnes if (drm_atomic_helper_damage_merged(old_state, state, &rect))
586229d9468SThomas Zimmermann gm12u320_fb_mark_dirty(state->fb, &shadow_plane_state->data[0], &rect);
58777b8cabfSNoralf Trønnes }
58877b8cabfSNoralf Trønnes
58977b8cabfSNoralf Trønnes static const struct drm_simple_display_pipe_funcs gm12u320_pipe_funcs = {
59077b8cabfSNoralf Trønnes .enable = gm12u320_pipe_enable,
59177b8cabfSNoralf Trønnes .disable = gm12u320_pipe_disable,
59277b8cabfSNoralf Trønnes .update = gm12u320_pipe_update,
5934ac0868dSThomas Zimmermann DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
59477b8cabfSNoralf Trønnes };
59577b8cabfSNoralf Trønnes
59677b8cabfSNoralf Trønnes static const uint32_t gm12u320_pipe_formats[] = {
59777b8cabfSNoralf Trønnes DRM_FORMAT_XRGB8888,
59877b8cabfSNoralf Trønnes };
59977b8cabfSNoralf Trønnes
60077b8cabfSNoralf Trønnes static const uint64_t gm12u320_pipe_modifiers[] = {
60177b8cabfSNoralf Trønnes DRM_FORMAT_MOD_LINEAR,
60277b8cabfSNoralf Trønnes DRM_FORMAT_MOD_INVALID
60377b8cabfSNoralf Trønnes };
60477b8cabfSNoralf Trønnes
605659ab7a4SThomas Zimmermann /*
606659ab7a4SThomas Zimmermann * FIXME: Dma-buf sharing requires DMA support by the importing device.
607659ab7a4SThomas Zimmermann * This function is a workaround to make USB devices work as well.
608659ab7a4SThomas Zimmermann * See todo.rst for how to fix the issue in the dma-buf framework.
609659ab7a4SThomas Zimmermann */
gm12u320_gem_prime_import(struct drm_device * dev,struct dma_buf * dma_buf)610659ab7a4SThomas Zimmermann static struct drm_gem_object *gm12u320_gem_prime_import(struct drm_device *dev,
611659ab7a4SThomas Zimmermann struct dma_buf *dma_buf)
612659ab7a4SThomas Zimmermann {
613659ab7a4SThomas Zimmermann struct gm12u320_device *gm12u320 = to_gm12u320(dev);
614659ab7a4SThomas Zimmermann
615659ab7a4SThomas Zimmermann if (!gm12u320->dmadev)
616659ab7a4SThomas Zimmermann return ERR_PTR(-ENODEV);
617659ab7a4SThomas Zimmermann
618659ab7a4SThomas Zimmermann return drm_gem_prime_import_dev(dev, dma_buf, gm12u320->dmadev);
619659ab7a4SThomas Zimmermann }
620659ab7a4SThomas Zimmermann
621eee9a2e0SGerd Hoffmann DEFINE_DRM_GEM_FOPS(gm12u320_fops);
62277b8cabfSNoralf Trønnes
62370a59dd8SDaniel Vetter static const struct drm_driver gm12u320_drm_driver = {
62477b8cabfSNoralf Trønnes .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
62577b8cabfSNoralf Trønnes
62677b8cabfSNoralf Trønnes .name = DRIVER_NAME,
62777b8cabfSNoralf Trønnes .desc = DRIVER_DESC,
62877b8cabfSNoralf Trønnes .date = DRIVER_DATE,
62977b8cabfSNoralf Trønnes .major = DRIVER_MAJOR,
63077b8cabfSNoralf Trønnes .minor = DRIVER_MINOR,
63177b8cabfSNoralf Trønnes
63277b8cabfSNoralf Trønnes .fops = &gm12u320_fops,
63377b8cabfSNoralf Trønnes DRM_GEM_SHMEM_DRIVER_OPS,
634659ab7a4SThomas Zimmermann .gem_prime_import = gm12u320_gem_prime_import,
63577b8cabfSNoralf Trønnes };
63677b8cabfSNoralf Trønnes
63777b8cabfSNoralf Trønnes static const struct drm_mode_config_funcs gm12u320_mode_config_funcs = {
63877b8cabfSNoralf Trønnes .fb_create = drm_gem_fb_create_with_dirty,
63977b8cabfSNoralf Trønnes .atomic_check = drm_atomic_helper_check,
64077b8cabfSNoralf Trønnes .atomic_commit = drm_atomic_helper_commit,
64177b8cabfSNoralf Trønnes };
64277b8cabfSNoralf Trønnes
gm12u320_usb_probe(struct usb_interface * interface,const struct usb_device_id * id)64377b8cabfSNoralf Trønnes static int gm12u320_usb_probe(struct usb_interface *interface,
64477b8cabfSNoralf Trønnes const struct usb_device_id *id)
64577b8cabfSNoralf Trønnes {
64677b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320;
64777b8cabfSNoralf Trønnes struct drm_device *dev;
64877b8cabfSNoralf Trønnes int ret;
64977b8cabfSNoralf Trønnes
65077b8cabfSNoralf Trønnes /*
65177b8cabfSNoralf Trønnes * The gm12u320 presents itself to the system as 2 usb mass-storage
65277b8cabfSNoralf Trønnes * interfaces, we only care about / need the first one.
65377b8cabfSNoralf Trønnes */
65477b8cabfSNoralf Trønnes if (interface->cur_altsetting->desc.bInterfaceNumber != 0)
65577b8cabfSNoralf Trønnes return -ENODEV;
65677b8cabfSNoralf Trønnes
6579213142dSDaniel Vetter gm12u320 = devm_drm_dev_alloc(&interface->dev, &gm12u320_drm_driver,
6589213142dSDaniel Vetter struct gm12u320_device, dev);
6599213142dSDaniel Vetter if (IS_ERR(gm12u320))
6609213142dSDaniel Vetter return PTR_ERR(gm12u320);
661659ab7a4SThomas Zimmermann dev = &gm12u320->dev;
662659ab7a4SThomas Zimmermann
663659ab7a4SThomas Zimmermann gm12u320->dmadev = usb_intf_get_dma_device(to_usb_interface(dev->dev));
664659ab7a4SThomas Zimmermann if (!gm12u320->dmadev)
665659ab7a4SThomas Zimmermann drm_warn(dev, "buffer sharing not supported"); /* not an error */
66677b8cabfSNoralf Trønnes
6678f2cb937SDaniel Vetter INIT_DELAYED_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work);
66877b8cabfSNoralf Trønnes mutex_init(&gm12u320->fb_update.lock);
66977b8cabfSNoralf Trønnes
67008373edcSDaniel Vetter ret = drmm_mode_config_init(dev);
67108373edcSDaniel Vetter if (ret)
672659ab7a4SThomas Zimmermann goto err_put_device;
67308373edcSDaniel Vetter
67477b8cabfSNoralf Trønnes dev->mode_config.min_width = GM12U320_USER_WIDTH;
67577b8cabfSNoralf Trønnes dev->mode_config.max_width = GM12U320_USER_WIDTH;
67677b8cabfSNoralf Trønnes dev->mode_config.min_height = GM12U320_HEIGHT;
67777b8cabfSNoralf Trønnes dev->mode_config.max_height = GM12U320_HEIGHT;
67877b8cabfSNoralf Trønnes dev->mode_config.funcs = &gm12u320_mode_config_funcs;
67977b8cabfSNoralf Trønnes
68077b8cabfSNoralf Trønnes ret = gm12u320_usb_alloc(gm12u320);
68177b8cabfSNoralf Trønnes if (ret)
682659ab7a4SThomas Zimmermann goto err_put_device;
68377b8cabfSNoralf Trønnes
68477b8cabfSNoralf Trønnes ret = gm12u320_set_ecomode(gm12u320);
68577b8cabfSNoralf Trønnes if (ret)
686659ab7a4SThomas Zimmermann goto err_put_device;
68777b8cabfSNoralf Trønnes
68877b8cabfSNoralf Trønnes ret = gm12u320_conn_init(gm12u320);
68977b8cabfSNoralf Trønnes if (ret)
690659ab7a4SThomas Zimmermann goto err_put_device;
69177b8cabfSNoralf Trønnes
69277b8cabfSNoralf Trønnes ret = drm_simple_display_pipe_init(&gm12u320->dev,
69377b8cabfSNoralf Trønnes &gm12u320->pipe,
69477b8cabfSNoralf Trønnes &gm12u320_pipe_funcs,
69577b8cabfSNoralf Trønnes gm12u320_pipe_formats,
69677b8cabfSNoralf Trønnes ARRAY_SIZE(gm12u320_pipe_formats),
69777b8cabfSNoralf Trønnes gm12u320_pipe_modifiers,
69877b8cabfSNoralf Trønnes &gm12u320->conn);
69977b8cabfSNoralf Trønnes if (ret)
700659ab7a4SThomas Zimmermann goto err_put_device;
70177b8cabfSNoralf Trønnes
70277b8cabfSNoralf Trønnes drm_mode_config_reset(dev);
70377b8cabfSNoralf Trønnes
70477b8cabfSNoralf Trønnes usb_set_intfdata(interface, dev);
70577b8cabfSNoralf Trønnes ret = drm_dev_register(dev, 0);
70677b8cabfSNoralf Trønnes if (ret)
707659ab7a4SThomas Zimmermann goto err_put_device;
70877b8cabfSNoralf Trønnes
7095bd79b70SThomas Zimmermann drm_fbdev_shmem_setup(dev, 0);
71077b8cabfSNoralf Trønnes
71177b8cabfSNoralf Trønnes return 0;
712659ab7a4SThomas Zimmermann
713659ab7a4SThomas Zimmermann err_put_device:
714659ab7a4SThomas Zimmermann put_device(gm12u320->dmadev);
715659ab7a4SThomas Zimmermann return ret;
71677b8cabfSNoralf Trønnes }
71777b8cabfSNoralf Trønnes
gm12u320_usb_disconnect(struct usb_interface * interface)71877b8cabfSNoralf Trønnes static void gm12u320_usb_disconnect(struct usb_interface *interface)
71977b8cabfSNoralf Trønnes {
72077b8cabfSNoralf Trønnes struct drm_device *dev = usb_get_intfdata(interface);
721659ab7a4SThomas Zimmermann struct gm12u320_device *gm12u320 = to_gm12u320(dev);
72277b8cabfSNoralf Trønnes
723659ab7a4SThomas Zimmermann put_device(gm12u320->dmadev);
724659ab7a4SThomas Zimmermann gm12u320->dmadev = NULL;
72577b8cabfSNoralf Trønnes drm_dev_unplug(dev);
7267ef64ed1SDaniel Vetter drm_atomic_helper_shutdown(dev);
72777b8cabfSNoralf Trønnes }
72877b8cabfSNoralf Trønnes
gm12u320_suspend(struct usb_interface * interface,pm_message_t message)72949eafb20SPaul Cercueil static int gm12u320_suspend(struct usb_interface *interface,
73077b8cabfSNoralf Trønnes pm_message_t message)
73177b8cabfSNoralf Trønnes {
73277b8cabfSNoralf Trønnes struct drm_device *dev = usb_get_intfdata(interface);
73377b8cabfSNoralf Trønnes
7347ef64ed1SDaniel Vetter return drm_mode_config_helper_suspend(dev);
73577b8cabfSNoralf Trønnes }
73677b8cabfSNoralf Trønnes
gm12u320_resume(struct usb_interface * interface)73749eafb20SPaul Cercueil static int gm12u320_resume(struct usb_interface *interface)
73877b8cabfSNoralf Trønnes {
73977b8cabfSNoralf Trønnes struct drm_device *dev = usb_get_intfdata(interface);
7407ced4801SDaniel Vetter struct gm12u320_device *gm12u320 = to_gm12u320(dev);
74177b8cabfSNoralf Trønnes
74277b8cabfSNoralf Trønnes gm12u320_set_ecomode(gm12u320);
74377b8cabfSNoralf Trønnes
7447ef64ed1SDaniel Vetter return drm_mode_config_helper_resume(dev);
74577b8cabfSNoralf Trønnes }
74677b8cabfSNoralf Trønnes
74777b8cabfSNoralf Trønnes static const struct usb_device_id id_table[] = {
74877b8cabfSNoralf Trønnes { USB_DEVICE(0x1de1, 0xc102) },
74977b8cabfSNoralf Trønnes {},
75077b8cabfSNoralf Trønnes };
75177b8cabfSNoralf Trønnes MODULE_DEVICE_TABLE(usb, id_table);
75277b8cabfSNoralf Trønnes
75377b8cabfSNoralf Trønnes static struct usb_driver gm12u320_usb_driver = {
75477b8cabfSNoralf Trønnes .name = "gm12u320",
75577b8cabfSNoralf Trønnes .probe = gm12u320_usb_probe,
75677b8cabfSNoralf Trønnes .disconnect = gm12u320_usb_disconnect,
75777b8cabfSNoralf Trønnes .id_table = id_table,
75849eafb20SPaul Cercueil .suspend = pm_ptr(gm12u320_suspend),
75949eafb20SPaul Cercueil .resume = pm_ptr(gm12u320_resume),
76049eafb20SPaul Cercueil .reset_resume = pm_ptr(gm12u320_resume),
76177b8cabfSNoralf Trønnes };
76277b8cabfSNoralf Trønnes
76377b8cabfSNoralf Trønnes module_usb_driver(gm12u320_usb_driver);
76477b8cabfSNoralf Trønnes MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
7652c232f9bSJeff Johnson MODULE_DESCRIPTION("GM12U320 driver for USB projectors");
76677b8cabfSNoralf Trønnes MODULE_LICENSE("GPL");
767