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/dma-buf.h> 777b8cabfSNoralf Trønnes #include <linux/module.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> 1577b8cabfSNoralf Trønnes #include <drm/drm_fb_helper.h> 1677b8cabfSNoralf Trønnes #include <drm/drm_file.h> 1777b8cabfSNoralf Trønnes #include <drm/drm_format_helper.h> 1877b8cabfSNoralf Trønnes #include <drm/drm_fourcc.h> 1977b8cabfSNoralf Trønnes #include <drm/drm_gem_shmem_helper.h> 2077b8cabfSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 2177b8cabfSNoralf Trønnes #include <drm/drm_ioctl.h> 22b6731025SDaniel Vetter #include <drm/drm_managed.h> 2377b8cabfSNoralf Trønnes #include <drm/drm_modeset_helper_vtables.h> 2477b8cabfSNoralf Trønnes #include <drm/drm_probe_helper.h> 2577b8cabfSNoralf Trønnes #include <drm/drm_simple_kms_helper.h> 2677b8cabfSNoralf Trønnes 2777b8cabfSNoralf Trønnes static bool eco_mode; 2877b8cabfSNoralf Trønnes module_param(eco_mode, bool, 0644); 2977b8cabfSNoralf Trønnes MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)"); 3077b8cabfSNoralf Trønnes 3177b8cabfSNoralf Trønnes #define DRIVER_NAME "gm12u320" 3277b8cabfSNoralf Trønnes #define DRIVER_DESC "Grain Media GM12U320 USB projector display" 3377b8cabfSNoralf Trønnes #define DRIVER_DATE "2019" 3477b8cabfSNoralf Trønnes #define DRIVER_MAJOR 1 3577b8cabfSNoralf Trønnes #define DRIVER_MINOR 0 3677b8cabfSNoralf Trønnes 3777b8cabfSNoralf Trønnes /* 3877b8cabfSNoralf Trønnes * The DLP has an actual width of 854 pixels, but that is not a multiple 3977b8cabfSNoralf Trønnes * of 8, breaking things left and right, so we export a width of 848. 4077b8cabfSNoralf Trønnes */ 4177b8cabfSNoralf Trønnes #define GM12U320_USER_WIDTH 848 4277b8cabfSNoralf Trønnes #define GM12U320_REAL_WIDTH 854 4377b8cabfSNoralf Trønnes #define GM12U320_HEIGHT 480 4477b8cabfSNoralf Trønnes 4577b8cabfSNoralf Trønnes #define GM12U320_BLOCK_COUNT 20 4677b8cabfSNoralf Trønnes 474abfa2e4SHans de Goede #define GM12U320_ERR(fmt, ...) \ 484abfa2e4SHans de Goede DRM_DEV_ERROR(&gm12u320->udev->dev, fmt, ##__VA_ARGS__) 494abfa2e4SHans de Goede 5077b8cabfSNoralf Trønnes #define MISC_RCV_EPT 1 5177b8cabfSNoralf Trønnes #define DATA_RCV_EPT 2 5277b8cabfSNoralf Trønnes #define DATA_SND_EPT 3 5377b8cabfSNoralf Trønnes #define MISC_SND_EPT 4 5477b8cabfSNoralf Trønnes 5577b8cabfSNoralf Trønnes #define DATA_BLOCK_HEADER_SIZE 84 5677b8cabfSNoralf Trønnes #define DATA_BLOCK_CONTENT_SIZE 64512 5777b8cabfSNoralf Trønnes #define DATA_BLOCK_FOOTER_SIZE 20 5877b8cabfSNoralf Trønnes #define DATA_BLOCK_SIZE (DATA_BLOCK_HEADER_SIZE + \ 5977b8cabfSNoralf Trønnes DATA_BLOCK_CONTENT_SIZE + \ 6077b8cabfSNoralf Trønnes DATA_BLOCK_FOOTER_SIZE) 6177b8cabfSNoralf Trønnes #define DATA_LAST_BLOCK_CONTENT_SIZE 4032 6277b8cabfSNoralf Trønnes #define DATA_LAST_BLOCK_SIZE (DATA_BLOCK_HEADER_SIZE + \ 6377b8cabfSNoralf Trønnes DATA_LAST_BLOCK_CONTENT_SIZE + \ 6477b8cabfSNoralf Trønnes DATA_BLOCK_FOOTER_SIZE) 6577b8cabfSNoralf Trønnes 6677b8cabfSNoralf Trønnes #define CMD_SIZE 31 6777b8cabfSNoralf Trønnes #define READ_STATUS_SIZE 13 6877b8cabfSNoralf Trønnes #define MISC_VALUE_SIZE 4 6977b8cabfSNoralf Trønnes 7077b8cabfSNoralf Trønnes #define CMD_TIMEOUT msecs_to_jiffies(200) 7177b8cabfSNoralf Trønnes #define DATA_TIMEOUT msecs_to_jiffies(1000) 7277b8cabfSNoralf Trønnes #define IDLE_TIMEOUT msecs_to_jiffies(2000) 7377b8cabfSNoralf Trønnes #define FIRST_FRAME_TIMEOUT msecs_to_jiffies(2000) 7477b8cabfSNoralf Trønnes 7577b8cabfSNoralf Trønnes #define MISC_REQ_GET_SET_ECO_A 0xff 7677b8cabfSNoralf Trønnes #define MISC_REQ_GET_SET_ECO_B 0x35 7777b8cabfSNoralf Trønnes /* Windows driver does once every second, with arg d = 1, other args 0 */ 7877b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN1_A 0xff 7977b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN1_B 0x38 8077b8cabfSNoralf Trønnes /* Windows driver does this on init, with arg a, b = 0, c = 0xa0, d = 4 */ 8177b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN2_A 0xa5 8277b8cabfSNoralf Trønnes #define MISC_REQ_UNKNOWN2_B 0x00 8377b8cabfSNoralf Trønnes 8477b8cabfSNoralf Trønnes struct gm12u320_device { 8577b8cabfSNoralf Trønnes struct drm_device dev; 8677b8cabfSNoralf Trønnes struct drm_simple_display_pipe pipe; 8777b8cabfSNoralf Trønnes struct drm_connector conn; 8877b8cabfSNoralf Trønnes struct usb_device *udev; 8977b8cabfSNoralf Trønnes unsigned char *cmd_buf; 9077b8cabfSNoralf Trønnes unsigned char *data_buf[GM12U320_BLOCK_COUNT]; 9177b8cabfSNoralf Trønnes struct { 9277b8cabfSNoralf Trønnes bool run; 9377b8cabfSNoralf Trønnes struct workqueue_struct *workq; 9477b8cabfSNoralf Trønnes struct work_struct work; 9577b8cabfSNoralf Trønnes wait_queue_head_t waitq; 9677b8cabfSNoralf Trønnes struct mutex lock; 9777b8cabfSNoralf Trønnes struct drm_framebuffer *fb; 9877b8cabfSNoralf Trønnes struct drm_rect rect; 9977b8cabfSNoralf Trønnes } fb_update; 10077b8cabfSNoralf Trønnes }; 10177b8cabfSNoralf Trønnes 10277b8cabfSNoralf Trønnes static const char cmd_data[CMD_SIZE] = { 10377b8cabfSNoralf Trønnes 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 10477b8cabfSNoralf Trønnes 0x68, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 10577b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x80, 0x00, 10677b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 10777b8cabfSNoralf Trønnes }; 10877b8cabfSNoralf Trønnes 10977b8cabfSNoralf Trønnes static const char cmd_draw[CMD_SIZE] = { 11077b8cabfSNoralf Trønnes 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 11177b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xfe, 11277b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0xc0, 0xd1, 0x05, 0x00, 0x40, 11377b8cabfSNoralf Trønnes 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 11477b8cabfSNoralf Trønnes }; 11577b8cabfSNoralf Trønnes 11677b8cabfSNoralf Trønnes static const char cmd_misc[CMD_SIZE] = { 11777b8cabfSNoralf Trønnes 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 11877b8cabfSNoralf Trønnes 0x04, 0x00, 0x00, 0x00, 0x80, 0x01, 0x10, 0xfd, 11977b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 12077b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 12177b8cabfSNoralf Trønnes }; 12277b8cabfSNoralf Trønnes 12377b8cabfSNoralf Trønnes static const char data_block_header[DATA_BLOCK_HEADER_SIZE] = { 12477b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12577b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12677b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12777b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12877b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 13377b8cabfSNoralf Trønnes 0x00, 0x04, 0x15, 0x00, 0x00, 0xfc, 0x00, 0x00, 13477b8cabfSNoralf Trønnes 0x01, 0x00, 0x00, 0xdb 13577b8cabfSNoralf Trønnes }; 13677b8cabfSNoralf Trønnes 13777b8cabfSNoralf Trønnes static const char data_last_block_header[DATA_BLOCK_HEADER_SIZE] = { 13877b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 13977b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14077b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14177b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14277b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 0xfb, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14777b8cabfSNoralf Trønnes 0x2a, 0x00, 0x20, 0x00, 0xc0, 0x0f, 0x00, 0x00, 14877b8cabfSNoralf Trønnes 0x01, 0x00, 0x00, 0xd7 14977b8cabfSNoralf Trønnes }; 15077b8cabfSNoralf Trønnes 15177b8cabfSNoralf Trønnes static const char data_block_footer[DATA_BLOCK_FOOTER_SIZE] = { 15277b8cabfSNoralf Trønnes 0xfb, 0x14, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 15377b8cabfSNoralf Trønnes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 15477b8cabfSNoralf Trønnes 0x80, 0x00, 0x00, 0x4f 15577b8cabfSNoralf Trønnes }; 15677b8cabfSNoralf Trønnes 15777b8cabfSNoralf Trønnes static int gm12u320_usb_alloc(struct gm12u320_device *gm12u320) 15877b8cabfSNoralf Trønnes { 15977b8cabfSNoralf Trønnes int i, block_size; 16077b8cabfSNoralf Trønnes const char *hdr; 16177b8cabfSNoralf Trønnes 16208373edcSDaniel Vetter gm12u320->cmd_buf = drmm_kmalloc(&gm12u320->dev, CMD_SIZE, GFP_KERNEL); 16377b8cabfSNoralf Trønnes if (!gm12u320->cmd_buf) 16477b8cabfSNoralf Trønnes return -ENOMEM; 16577b8cabfSNoralf Trønnes 16677b8cabfSNoralf Trønnes for (i = 0; i < GM12U320_BLOCK_COUNT; i++) { 16777b8cabfSNoralf Trønnes if (i == GM12U320_BLOCK_COUNT - 1) { 16877b8cabfSNoralf Trønnes block_size = DATA_LAST_BLOCK_SIZE; 16977b8cabfSNoralf Trønnes hdr = data_last_block_header; 17077b8cabfSNoralf Trønnes } else { 17177b8cabfSNoralf Trønnes block_size = DATA_BLOCK_SIZE; 17277b8cabfSNoralf Trønnes hdr = data_block_header; 17377b8cabfSNoralf Trønnes } 17477b8cabfSNoralf Trønnes 17508373edcSDaniel Vetter gm12u320->data_buf[i] = drmm_kzalloc(&gm12u320->dev, 17608373edcSDaniel Vetter block_size, GFP_KERNEL); 17777b8cabfSNoralf Trønnes if (!gm12u320->data_buf[i]) 17877b8cabfSNoralf Trønnes return -ENOMEM; 17977b8cabfSNoralf Trønnes 18077b8cabfSNoralf Trønnes memcpy(gm12u320->data_buf[i], hdr, DATA_BLOCK_HEADER_SIZE); 18177b8cabfSNoralf Trønnes memcpy(gm12u320->data_buf[i] + 18277b8cabfSNoralf Trønnes (block_size - DATA_BLOCK_FOOTER_SIZE), 18377b8cabfSNoralf Trønnes data_block_footer, DATA_BLOCK_FOOTER_SIZE); 18477b8cabfSNoralf Trønnes } 18577b8cabfSNoralf Trønnes 18677b8cabfSNoralf Trønnes gm12u320->fb_update.workq = create_singlethread_workqueue(DRIVER_NAME); 18777b8cabfSNoralf Trønnes if (!gm12u320->fb_update.workq) 18877b8cabfSNoralf Trønnes return -ENOMEM; 18977b8cabfSNoralf Trønnes 19077b8cabfSNoralf Trønnes return 0; 19177b8cabfSNoralf Trønnes } 19277b8cabfSNoralf Trønnes 19377b8cabfSNoralf Trønnes static void gm12u320_usb_free(struct gm12u320_device *gm12u320) 19477b8cabfSNoralf Trønnes { 19577b8cabfSNoralf Trønnes if (gm12u320->fb_update.workq) 19677b8cabfSNoralf Trønnes destroy_workqueue(gm12u320->fb_update.workq); 19777b8cabfSNoralf Trønnes } 19877b8cabfSNoralf Trønnes 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 { 20377b8cabfSNoralf Trønnes int ret, len; 20477b8cabfSNoralf Trønnes 20577b8cabfSNoralf Trønnes memcpy(gm12u320->cmd_buf, &cmd_misc, CMD_SIZE); 20677b8cabfSNoralf Trønnes gm12u320->cmd_buf[20] = req_a; 20777b8cabfSNoralf Trønnes gm12u320->cmd_buf[21] = req_b; 20877b8cabfSNoralf Trønnes gm12u320->cmd_buf[22] = arg_a; 20977b8cabfSNoralf Trønnes gm12u320->cmd_buf[23] = arg_b; 21077b8cabfSNoralf Trønnes gm12u320->cmd_buf[24] = arg_c; 21177b8cabfSNoralf Trønnes gm12u320->cmd_buf[25] = arg_d; 21277b8cabfSNoralf Trønnes 21377b8cabfSNoralf Trønnes /* Send request */ 21477b8cabfSNoralf Trønnes ret = usb_bulk_msg(gm12u320->udev, 21577b8cabfSNoralf Trønnes usb_sndbulkpipe(gm12u320->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 */ 22377b8cabfSNoralf Trønnes ret = usb_bulk_msg(gm12u320->udev, 22477b8cabfSNoralf Trønnes usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT), 22577b8cabfSNoralf Trønnes gm12u320->cmd_buf, MISC_VALUE_SIZE, &len, 22677b8cabfSNoralf Trønnes DATA_TIMEOUT); 22777b8cabfSNoralf Trønnes if (ret || len != MISC_VALUE_SIZE) { 2284abfa2e4SHans de Goede GM12U320_ERR("Misc. value error %d\n", ret); 22977b8cabfSNoralf Trønnes return -EIO; 23077b8cabfSNoralf Trønnes } 23177b8cabfSNoralf Trønnes /* cmd_buf[0] now contains the read value, which we don't use */ 23277b8cabfSNoralf Trønnes 23377b8cabfSNoralf Trønnes /* Read status */ 23477b8cabfSNoralf Trønnes ret = usb_bulk_msg(gm12u320->udev, 23577b8cabfSNoralf Trønnes usb_rcvbulkpipe(gm12u320->udev, MISC_RCV_EPT), 23677b8cabfSNoralf Trønnes gm12u320->cmd_buf, READ_STATUS_SIZE, &len, 23777b8cabfSNoralf Trønnes CMD_TIMEOUT); 23877b8cabfSNoralf Trønnes if (ret || len != READ_STATUS_SIZE) { 2394abfa2e4SHans de Goede GM12U320_ERR("Misc. status error %d\n", ret); 24077b8cabfSNoralf Trønnes return -EIO; 24177b8cabfSNoralf Trønnes } 24277b8cabfSNoralf Trønnes 24377b8cabfSNoralf Trønnes return 0; 24477b8cabfSNoralf Trønnes } 24577b8cabfSNoralf Trønnes 24677b8cabfSNoralf Trønnes static void gm12u320_32bpp_to_24bpp_packed(u8 *dst, u8 *src, int len) 24777b8cabfSNoralf Trønnes { 24877b8cabfSNoralf Trønnes while (len--) { 24977b8cabfSNoralf Trønnes *dst++ = *src++; 25077b8cabfSNoralf Trønnes *dst++ = *src++; 25177b8cabfSNoralf Trønnes *dst++ = *src++; 25277b8cabfSNoralf Trønnes src++; 25377b8cabfSNoralf Trønnes } 25477b8cabfSNoralf Trønnes } 25577b8cabfSNoralf Trønnes 25677b8cabfSNoralf Trønnes static void gm12u320_copy_fb_to_blocks(struct gm12u320_device *gm12u320) 25777b8cabfSNoralf Trønnes { 25877b8cabfSNoralf Trønnes int block, dst_offset, len, remain, ret, x1, x2, y1, y2; 25977b8cabfSNoralf Trønnes struct drm_framebuffer *fb; 26077b8cabfSNoralf Trønnes void *vaddr; 26177b8cabfSNoralf Trønnes u8 *src; 26277b8cabfSNoralf Trønnes 26377b8cabfSNoralf Trønnes mutex_lock(&gm12u320->fb_update.lock); 26477b8cabfSNoralf Trønnes 26577b8cabfSNoralf Trønnes if (!gm12u320->fb_update.fb) 26677b8cabfSNoralf Trønnes goto unlock; 26777b8cabfSNoralf Trønnes 26877b8cabfSNoralf Trønnes fb = gm12u320->fb_update.fb; 26977b8cabfSNoralf Trønnes x1 = gm12u320->fb_update.rect.x1; 27077b8cabfSNoralf Trønnes x2 = gm12u320->fb_update.rect.x2; 27177b8cabfSNoralf Trønnes y1 = gm12u320->fb_update.rect.y1; 27277b8cabfSNoralf Trønnes y2 = gm12u320->fb_update.rect.y2; 27377b8cabfSNoralf Trønnes 27477b8cabfSNoralf Trønnes vaddr = drm_gem_shmem_vmap(fb->obj[0]); 27577b8cabfSNoralf Trønnes if (IS_ERR(vaddr)) { 2764abfa2e4SHans de Goede GM12U320_ERR("failed to vmap fb: %ld\n", PTR_ERR(vaddr)); 27777b8cabfSNoralf Trønnes goto put_fb; 27877b8cabfSNoralf Trønnes } 27977b8cabfSNoralf Trønnes 28077b8cabfSNoralf Trønnes if (fb->obj[0]->import_attach) { 28177b8cabfSNoralf Trønnes ret = dma_buf_begin_cpu_access( 28277b8cabfSNoralf Trønnes fb->obj[0]->import_attach->dmabuf, DMA_FROM_DEVICE); 28377b8cabfSNoralf Trønnes if (ret) { 2844abfa2e4SHans de Goede GM12U320_ERR("dma_buf_begin_cpu_access err: %d\n", ret); 28577b8cabfSNoralf Trønnes goto vunmap; 28677b8cabfSNoralf Trønnes } 28777b8cabfSNoralf Trønnes } 28877b8cabfSNoralf Trønnes 28977b8cabfSNoralf Trønnes src = vaddr + y1 * fb->pitches[0] + x1 * 4; 29077b8cabfSNoralf Trønnes 29177b8cabfSNoralf Trønnes x1 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2; 29277b8cabfSNoralf Trønnes x2 += (GM12U320_REAL_WIDTH - GM12U320_USER_WIDTH) / 2; 29377b8cabfSNoralf Trønnes 29477b8cabfSNoralf Trønnes for (; y1 < y2; y1++) { 29577b8cabfSNoralf Trønnes remain = 0; 29677b8cabfSNoralf Trønnes len = (x2 - x1) * 3; 29777b8cabfSNoralf Trønnes dst_offset = (y1 * GM12U320_REAL_WIDTH + x1) * 3; 29877b8cabfSNoralf Trønnes block = dst_offset / DATA_BLOCK_CONTENT_SIZE; 29977b8cabfSNoralf Trønnes dst_offset %= DATA_BLOCK_CONTENT_SIZE; 30077b8cabfSNoralf Trønnes 30177b8cabfSNoralf Trønnes if ((dst_offset + len) > DATA_BLOCK_CONTENT_SIZE) { 30277b8cabfSNoralf Trønnes remain = dst_offset + len - DATA_BLOCK_CONTENT_SIZE; 30377b8cabfSNoralf Trønnes len = DATA_BLOCK_CONTENT_SIZE - dst_offset; 30477b8cabfSNoralf Trønnes } 30577b8cabfSNoralf Trønnes 30677b8cabfSNoralf Trønnes dst_offset += DATA_BLOCK_HEADER_SIZE; 30777b8cabfSNoralf Trønnes len /= 3; 30877b8cabfSNoralf Trønnes 30977b8cabfSNoralf Trønnes gm12u320_32bpp_to_24bpp_packed( 31077b8cabfSNoralf Trønnes gm12u320->data_buf[block] + dst_offset, 31177b8cabfSNoralf Trønnes src, len); 31277b8cabfSNoralf Trønnes 31377b8cabfSNoralf Trønnes if (remain) { 31477b8cabfSNoralf Trønnes block++; 31577b8cabfSNoralf Trønnes dst_offset = DATA_BLOCK_HEADER_SIZE; 31677b8cabfSNoralf Trønnes gm12u320_32bpp_to_24bpp_packed( 31777b8cabfSNoralf Trønnes gm12u320->data_buf[block] + dst_offset, 31877b8cabfSNoralf Trønnes src + len * 4, remain / 3); 31977b8cabfSNoralf Trønnes } 32077b8cabfSNoralf Trønnes src += fb->pitches[0]; 32177b8cabfSNoralf Trønnes } 32277b8cabfSNoralf Trønnes 32377b8cabfSNoralf Trønnes if (fb->obj[0]->import_attach) { 32477b8cabfSNoralf Trønnes ret = dma_buf_end_cpu_access(fb->obj[0]->import_attach->dmabuf, 32577b8cabfSNoralf Trønnes DMA_FROM_DEVICE); 32677b8cabfSNoralf Trønnes if (ret) 3274abfa2e4SHans de Goede GM12U320_ERR("dma_buf_end_cpu_access err: %d\n", ret); 32877b8cabfSNoralf Trønnes } 32977b8cabfSNoralf Trønnes vunmap: 33077b8cabfSNoralf Trønnes drm_gem_shmem_vunmap(fb->obj[0], vaddr); 33177b8cabfSNoralf Trønnes put_fb: 33277b8cabfSNoralf Trønnes drm_framebuffer_put(fb); 33377b8cabfSNoralf Trønnes gm12u320->fb_update.fb = NULL; 33477b8cabfSNoralf Trønnes unlock: 33577b8cabfSNoralf Trønnes mutex_unlock(&gm12u320->fb_update.lock); 33677b8cabfSNoralf Trønnes } 33777b8cabfSNoralf Trønnes 33877b8cabfSNoralf Trønnes static void gm12u320_fb_update_work(struct work_struct *work) 33977b8cabfSNoralf Trønnes { 34077b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320 = 34177b8cabfSNoralf Trønnes container_of(work, struct gm12u320_device, fb_update.work); 34277b8cabfSNoralf Trønnes int draw_status_timeout = FIRST_FRAME_TIMEOUT; 34377b8cabfSNoralf Trønnes int block, block_size, len; 34477b8cabfSNoralf Trønnes int frame = 0; 34577b8cabfSNoralf Trønnes int ret = 0; 34677b8cabfSNoralf Trønnes 34777b8cabfSNoralf Trønnes while (gm12u320->fb_update.run) { 34877b8cabfSNoralf Trønnes gm12u320_copy_fb_to_blocks(gm12u320); 34977b8cabfSNoralf Trønnes 35077b8cabfSNoralf Trønnes for (block = 0; block < GM12U320_BLOCK_COUNT; block++) { 35177b8cabfSNoralf Trønnes if (block == GM12U320_BLOCK_COUNT - 1) 35277b8cabfSNoralf Trønnes block_size = DATA_LAST_BLOCK_SIZE; 35377b8cabfSNoralf Trønnes else 35477b8cabfSNoralf Trønnes block_size = DATA_BLOCK_SIZE; 35577b8cabfSNoralf Trønnes 35677b8cabfSNoralf Trønnes /* Send data command to device */ 35777b8cabfSNoralf Trønnes memcpy(gm12u320->cmd_buf, cmd_data, CMD_SIZE); 35877b8cabfSNoralf Trønnes gm12u320->cmd_buf[8] = block_size & 0xff; 35977b8cabfSNoralf Trønnes gm12u320->cmd_buf[9] = block_size >> 8; 36077b8cabfSNoralf Trønnes gm12u320->cmd_buf[20] = 0xfc - block * 4; 36177b8cabfSNoralf Trønnes gm12u320->cmd_buf[21] = block | (frame << 7); 36277b8cabfSNoralf Trønnes 36377b8cabfSNoralf Trønnes ret = usb_bulk_msg(gm12u320->udev, 36477b8cabfSNoralf Trønnes usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), 36577b8cabfSNoralf Trønnes gm12u320->cmd_buf, CMD_SIZE, &len, 36677b8cabfSNoralf Trønnes CMD_TIMEOUT); 36777b8cabfSNoralf Trønnes if (ret || len != CMD_SIZE) 36877b8cabfSNoralf Trønnes goto err; 36977b8cabfSNoralf Trønnes 37077b8cabfSNoralf Trønnes /* Send data block to device */ 37177b8cabfSNoralf Trønnes ret = usb_bulk_msg(gm12u320->udev, 37277b8cabfSNoralf Trønnes usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), 37377b8cabfSNoralf Trønnes gm12u320->data_buf[block], block_size, 37477b8cabfSNoralf Trønnes &len, DATA_TIMEOUT); 37577b8cabfSNoralf Trønnes if (ret || len != block_size) 37677b8cabfSNoralf Trønnes goto err; 37777b8cabfSNoralf Trønnes 37877b8cabfSNoralf Trønnes /* Read status */ 37977b8cabfSNoralf Trønnes ret = usb_bulk_msg(gm12u320->udev, 38077b8cabfSNoralf Trønnes usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), 38177b8cabfSNoralf Trønnes gm12u320->cmd_buf, READ_STATUS_SIZE, &len, 38277b8cabfSNoralf Trønnes CMD_TIMEOUT); 38377b8cabfSNoralf Trønnes if (ret || len != READ_STATUS_SIZE) 38477b8cabfSNoralf Trønnes goto err; 38577b8cabfSNoralf Trønnes } 38677b8cabfSNoralf Trønnes 38777b8cabfSNoralf Trønnes /* Send draw command to device */ 38877b8cabfSNoralf Trønnes memcpy(gm12u320->cmd_buf, cmd_draw, CMD_SIZE); 38977b8cabfSNoralf Trønnes ret = usb_bulk_msg(gm12u320->udev, 39077b8cabfSNoralf Trønnes usb_sndbulkpipe(gm12u320->udev, DATA_SND_EPT), 39177b8cabfSNoralf Trønnes gm12u320->cmd_buf, CMD_SIZE, &len, CMD_TIMEOUT); 39277b8cabfSNoralf Trønnes if (ret || len != CMD_SIZE) 39377b8cabfSNoralf Trønnes goto err; 39477b8cabfSNoralf Trønnes 39577b8cabfSNoralf Trønnes /* Read status */ 39677b8cabfSNoralf Trønnes ret = usb_bulk_msg(gm12u320->udev, 39777b8cabfSNoralf Trønnes usb_rcvbulkpipe(gm12u320->udev, DATA_RCV_EPT), 39877b8cabfSNoralf Trønnes gm12u320->cmd_buf, READ_STATUS_SIZE, &len, 39977b8cabfSNoralf Trønnes draw_status_timeout); 40077b8cabfSNoralf Trønnes if (ret || len != READ_STATUS_SIZE) 40177b8cabfSNoralf Trønnes goto err; 40277b8cabfSNoralf Trønnes 40377b8cabfSNoralf Trønnes draw_status_timeout = CMD_TIMEOUT; 40477b8cabfSNoralf Trønnes frame = !frame; 40577b8cabfSNoralf Trønnes 40677b8cabfSNoralf Trønnes /* 40777b8cabfSNoralf Trønnes * We must draw a frame every 2s otherwise the projector 40877b8cabfSNoralf Trønnes * switches back to showing its logo. 40977b8cabfSNoralf Trønnes */ 41077b8cabfSNoralf Trønnes wait_event_timeout(gm12u320->fb_update.waitq, 4119b61db1aSHans de Goede !gm12u320->fb_update.run || 4129b61db1aSHans de Goede gm12u320->fb_update.fb != NULL, 41377b8cabfSNoralf Trønnes IDLE_TIMEOUT); 41477b8cabfSNoralf Trønnes } 41577b8cabfSNoralf Trønnes return; 41677b8cabfSNoralf Trønnes err: 41777b8cabfSNoralf Trønnes /* Do not log errors caused by module unload or device unplug */ 418ac9fd659SHans de Goede if (ret != -ENODEV && ret != -ECONNRESET && ret != -ESHUTDOWN) 4194abfa2e4SHans de Goede GM12U320_ERR("Frame update error: %d\n", ret); 42077b8cabfSNoralf Trønnes } 42177b8cabfSNoralf Trønnes 42277b8cabfSNoralf Trønnes static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb, 42377b8cabfSNoralf Trønnes struct drm_rect *dirty) 42477b8cabfSNoralf Trønnes { 42577b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320 = fb->dev->dev_private; 42677b8cabfSNoralf Trønnes struct drm_framebuffer *old_fb = NULL; 42777b8cabfSNoralf Trønnes bool wakeup = false; 42877b8cabfSNoralf Trønnes 42977b8cabfSNoralf Trønnes mutex_lock(&gm12u320->fb_update.lock); 43077b8cabfSNoralf Trønnes 43177b8cabfSNoralf Trønnes if (gm12u320->fb_update.fb != fb) { 43277b8cabfSNoralf Trønnes old_fb = gm12u320->fb_update.fb; 43377b8cabfSNoralf Trønnes drm_framebuffer_get(fb); 43477b8cabfSNoralf Trønnes gm12u320->fb_update.fb = fb; 43577b8cabfSNoralf Trønnes gm12u320->fb_update.rect = *dirty; 43677b8cabfSNoralf Trønnes wakeup = true; 43777b8cabfSNoralf Trønnes } else { 43877b8cabfSNoralf Trønnes struct drm_rect *rect = &gm12u320->fb_update.rect; 43977b8cabfSNoralf Trønnes 44077b8cabfSNoralf Trønnes rect->x1 = min(rect->x1, dirty->x1); 44177b8cabfSNoralf Trønnes rect->y1 = min(rect->y1, dirty->y1); 44277b8cabfSNoralf Trønnes rect->x2 = max(rect->x2, dirty->x2); 44377b8cabfSNoralf Trønnes rect->y2 = max(rect->y2, dirty->y2); 44477b8cabfSNoralf Trønnes } 44577b8cabfSNoralf Trønnes 44677b8cabfSNoralf Trønnes mutex_unlock(&gm12u320->fb_update.lock); 44777b8cabfSNoralf Trønnes 44877b8cabfSNoralf Trønnes if (wakeup) 44977b8cabfSNoralf Trønnes wake_up(&gm12u320->fb_update.waitq); 45077b8cabfSNoralf Trønnes 45177b8cabfSNoralf Trønnes if (old_fb) 45277b8cabfSNoralf Trønnes drm_framebuffer_put(old_fb); 45377b8cabfSNoralf Trønnes } 45477b8cabfSNoralf Trønnes 45577b8cabfSNoralf Trønnes static void gm12u320_start_fb_update(struct gm12u320_device *gm12u320) 45677b8cabfSNoralf Trønnes { 45777b8cabfSNoralf Trønnes mutex_lock(&gm12u320->fb_update.lock); 45877b8cabfSNoralf Trønnes gm12u320->fb_update.run = true; 45977b8cabfSNoralf Trønnes mutex_unlock(&gm12u320->fb_update.lock); 46077b8cabfSNoralf Trønnes 46177b8cabfSNoralf Trønnes queue_work(gm12u320->fb_update.workq, &gm12u320->fb_update.work); 46277b8cabfSNoralf Trønnes } 46377b8cabfSNoralf Trønnes 46477b8cabfSNoralf Trønnes static void gm12u320_stop_fb_update(struct gm12u320_device *gm12u320) 46577b8cabfSNoralf Trønnes { 46677b8cabfSNoralf Trønnes mutex_lock(&gm12u320->fb_update.lock); 46777b8cabfSNoralf Trønnes gm12u320->fb_update.run = false; 46877b8cabfSNoralf Trønnes mutex_unlock(&gm12u320->fb_update.lock); 46977b8cabfSNoralf Trønnes 47077b8cabfSNoralf Trønnes wake_up(&gm12u320->fb_update.waitq); 47177b8cabfSNoralf Trønnes cancel_work_sync(&gm12u320->fb_update.work); 47277b8cabfSNoralf Trønnes 47377b8cabfSNoralf Trønnes mutex_lock(&gm12u320->fb_update.lock); 47477b8cabfSNoralf Trønnes if (gm12u320->fb_update.fb) { 47577b8cabfSNoralf Trønnes drm_framebuffer_put(gm12u320->fb_update.fb); 47677b8cabfSNoralf Trønnes gm12u320->fb_update.fb = NULL; 47777b8cabfSNoralf Trønnes } 47877b8cabfSNoralf Trønnes mutex_unlock(&gm12u320->fb_update.lock); 47977b8cabfSNoralf Trønnes } 48077b8cabfSNoralf Trønnes 48177b8cabfSNoralf Trønnes static int gm12u320_set_ecomode(struct gm12u320_device *gm12u320) 48277b8cabfSNoralf Trønnes { 48377b8cabfSNoralf Trønnes return gm12u320_misc_request(gm12u320, MISC_REQ_GET_SET_ECO_A, 48477b8cabfSNoralf Trønnes MISC_REQ_GET_SET_ECO_B, 0x01 /* set */, 48577b8cabfSNoralf Trønnes eco_mode ? 0x01 : 0x00, 0x00, 0x01); 48677b8cabfSNoralf Trønnes } 48777b8cabfSNoralf Trønnes 48877b8cabfSNoralf Trønnes /* ------------------------------------------------------------------ */ 48977b8cabfSNoralf Trønnes /* gm12u320 connector */ 49077b8cabfSNoralf Trønnes 49177b8cabfSNoralf Trønnes /* 49277b8cabfSNoralf Trønnes * We use fake EDID info so that userspace know that it is dealing with 49377b8cabfSNoralf Trønnes * an Acer projector, rather then listing this as an "unknown" monitor. 49477b8cabfSNoralf Trønnes * Note this assumes this driver is only ever used with the Acer C120, if we 49577b8cabfSNoralf Trønnes * add support for other devices the vendor and model should be parameterized. 49677b8cabfSNoralf Trønnes */ 49777b8cabfSNoralf Trønnes static struct edid gm12u320_edid = { 49877b8cabfSNoralf Trønnes .header = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }, 49977b8cabfSNoralf Trønnes .mfg_id = { 0x04, 0x72 }, /* "ACR" */ 50077b8cabfSNoralf Trønnes .prod_code = { 0x20, 0xc1 }, /* C120h */ 50177b8cabfSNoralf Trønnes .serial = 0xaa55aa55, 50277b8cabfSNoralf Trønnes .mfg_week = 1, 50377b8cabfSNoralf Trønnes .mfg_year = 16, 50477b8cabfSNoralf Trønnes .version = 1, /* EDID 1.3 */ 50577b8cabfSNoralf Trønnes .revision = 3, /* EDID 1.3 */ 50677b8cabfSNoralf Trønnes .input = 0x08, /* Analog input */ 50777b8cabfSNoralf Trønnes .features = 0x0a, /* Pref timing in DTD 1 */ 50877b8cabfSNoralf Trønnes .standard_timings = { { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 }, 50977b8cabfSNoralf Trønnes { 1, 1 }, { 1, 1 }, { 1, 1 }, { 1, 1 } }, 51077b8cabfSNoralf Trønnes .detailed_timings = { { 51177b8cabfSNoralf Trønnes .pixel_clock = 3383, 51277b8cabfSNoralf Trønnes /* hactive = 848, hblank = 256 */ 51377b8cabfSNoralf Trønnes .data.pixel_data.hactive_lo = 0x50, 51477b8cabfSNoralf Trønnes .data.pixel_data.hblank_lo = 0x00, 51577b8cabfSNoralf Trønnes .data.pixel_data.hactive_hblank_hi = 0x31, 51677b8cabfSNoralf Trønnes /* vactive = 480, vblank = 28 */ 51777b8cabfSNoralf Trønnes .data.pixel_data.vactive_lo = 0xe0, 51877b8cabfSNoralf Trønnes .data.pixel_data.vblank_lo = 0x1c, 51977b8cabfSNoralf Trønnes .data.pixel_data.vactive_vblank_hi = 0x10, 52077b8cabfSNoralf Trønnes /* hsync offset 40 pw 128, vsync offset 1 pw 4 */ 52177b8cabfSNoralf Trønnes .data.pixel_data.hsync_offset_lo = 0x28, 52277b8cabfSNoralf Trønnes .data.pixel_data.hsync_pulse_width_lo = 0x80, 52377b8cabfSNoralf Trønnes .data.pixel_data.vsync_offset_pulse_width_lo = 0x14, 52477b8cabfSNoralf Trønnes .data.pixel_data.hsync_vsync_offset_pulse_width_hi = 0x00, 52577b8cabfSNoralf Trønnes /* Digital separate syncs, hsync+, vsync+ */ 52677b8cabfSNoralf Trønnes .data.pixel_data.misc = 0x1e, 52777b8cabfSNoralf Trønnes }, { 52877b8cabfSNoralf Trønnes .pixel_clock = 0, 52977b8cabfSNoralf Trønnes .data.other_data.type = 0xfd, /* Monitor ranges */ 53077b8cabfSNoralf Trønnes .data.other_data.data.range.min_vfreq = 59, 53177b8cabfSNoralf Trønnes .data.other_data.data.range.max_vfreq = 61, 53277b8cabfSNoralf Trønnes .data.other_data.data.range.min_hfreq_khz = 29, 53377b8cabfSNoralf Trønnes .data.other_data.data.range.max_hfreq_khz = 32, 53477b8cabfSNoralf Trønnes .data.other_data.data.range.pixel_clock_mhz = 4, /* 40 MHz */ 53577b8cabfSNoralf Trønnes .data.other_data.data.range.flags = 0, 53677b8cabfSNoralf Trønnes .data.other_data.data.range.formula.cvt = { 53777b8cabfSNoralf Trønnes 0xa0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, 53877b8cabfSNoralf Trønnes }, { 53977b8cabfSNoralf Trønnes .pixel_clock = 0, 54077b8cabfSNoralf Trønnes .data.other_data.type = 0xfc, /* Model string */ 54177b8cabfSNoralf Trønnes .data.other_data.data.str.str = { 54277b8cabfSNoralf Trønnes 'P', 'r', 'o', 'j', 'e', 'c', 't', 'o', 'r', '\n', 54377b8cabfSNoralf Trønnes ' ', ' ', ' ' }, 54477b8cabfSNoralf Trønnes }, { 54577b8cabfSNoralf Trønnes .pixel_clock = 0, 54677b8cabfSNoralf Trønnes .data.other_data.type = 0xfe, /* Unspecified text / padding */ 54777b8cabfSNoralf Trønnes .data.other_data.data.str.str = { 54877b8cabfSNoralf Trønnes '\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 54977b8cabfSNoralf Trønnes ' ', ' ', ' ' }, 55077b8cabfSNoralf Trønnes } }, 55177b8cabfSNoralf Trønnes .checksum = 0x13, 55277b8cabfSNoralf Trønnes }; 55377b8cabfSNoralf Trønnes 55477b8cabfSNoralf Trønnes static int gm12u320_conn_get_modes(struct drm_connector *connector) 55577b8cabfSNoralf Trønnes { 55677b8cabfSNoralf Trønnes drm_connector_update_edid_property(connector, &gm12u320_edid); 55777b8cabfSNoralf Trønnes return drm_add_edid_modes(connector, &gm12u320_edid); 55877b8cabfSNoralf Trønnes } 55977b8cabfSNoralf Trønnes 56077b8cabfSNoralf Trønnes static const struct drm_connector_helper_funcs gm12u320_conn_helper_funcs = { 56177b8cabfSNoralf Trønnes .get_modes = gm12u320_conn_get_modes, 56277b8cabfSNoralf Trønnes }; 56377b8cabfSNoralf Trønnes 56477b8cabfSNoralf Trønnes static const struct drm_connector_funcs gm12u320_conn_funcs = { 56577b8cabfSNoralf Trønnes .fill_modes = drm_helper_probe_single_connector_modes, 56677b8cabfSNoralf Trønnes .destroy = drm_connector_cleanup, 56777b8cabfSNoralf Trønnes .reset = drm_atomic_helper_connector_reset, 56877b8cabfSNoralf Trønnes .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 56977b8cabfSNoralf Trønnes .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 57077b8cabfSNoralf Trønnes }; 57177b8cabfSNoralf Trønnes 57277b8cabfSNoralf Trønnes static int gm12u320_conn_init(struct gm12u320_device *gm12u320) 57377b8cabfSNoralf Trønnes { 57477b8cabfSNoralf Trønnes drm_connector_helper_add(&gm12u320->conn, &gm12u320_conn_helper_funcs); 57577b8cabfSNoralf Trønnes return drm_connector_init(&gm12u320->dev, &gm12u320->conn, 57677b8cabfSNoralf Trønnes &gm12u320_conn_funcs, DRM_MODE_CONNECTOR_VGA); 57777b8cabfSNoralf Trønnes } 57877b8cabfSNoralf Trønnes 57977b8cabfSNoralf Trønnes /* ------------------------------------------------------------------ */ 58077b8cabfSNoralf Trønnes /* gm12u320 (simple) display pipe */ 58177b8cabfSNoralf Trønnes 58277b8cabfSNoralf Trønnes static void gm12u320_pipe_enable(struct drm_simple_display_pipe *pipe, 58377b8cabfSNoralf Trønnes struct drm_crtc_state *crtc_state, 58477b8cabfSNoralf Trønnes struct drm_plane_state *plane_state) 58577b8cabfSNoralf Trønnes { 58677b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private; 58777b8cabfSNoralf Trønnes struct drm_rect rect = { 0, 0, GM12U320_USER_WIDTH, GM12U320_HEIGHT }; 58877b8cabfSNoralf Trønnes 58977b8cabfSNoralf Trønnes gm12u320_fb_mark_dirty(plane_state->fb, &rect); 59077b8cabfSNoralf Trønnes gm12u320_start_fb_update(gm12u320); 59177b8cabfSNoralf Trønnes } 59277b8cabfSNoralf Trønnes 59377b8cabfSNoralf Trønnes static void gm12u320_pipe_disable(struct drm_simple_display_pipe *pipe) 59477b8cabfSNoralf Trønnes { 59577b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320 = pipe->crtc.dev->dev_private; 59677b8cabfSNoralf Trønnes 59777b8cabfSNoralf Trønnes gm12u320_stop_fb_update(gm12u320); 59877b8cabfSNoralf Trønnes } 59977b8cabfSNoralf Trønnes 60077b8cabfSNoralf Trønnes static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe, 60177b8cabfSNoralf Trønnes struct drm_plane_state *old_state) 60277b8cabfSNoralf Trønnes { 60377b8cabfSNoralf Trønnes struct drm_plane_state *state = pipe->plane.state; 60477b8cabfSNoralf Trønnes struct drm_rect rect; 60577b8cabfSNoralf Trønnes 60677b8cabfSNoralf Trønnes if (drm_atomic_helper_damage_merged(old_state, state, &rect)) 60777b8cabfSNoralf Trønnes gm12u320_fb_mark_dirty(pipe->plane.state->fb, &rect); 60877b8cabfSNoralf Trønnes } 60977b8cabfSNoralf Trønnes 61077b8cabfSNoralf Trønnes static const struct drm_simple_display_pipe_funcs gm12u320_pipe_funcs = { 61177b8cabfSNoralf Trønnes .enable = gm12u320_pipe_enable, 61277b8cabfSNoralf Trønnes .disable = gm12u320_pipe_disable, 61377b8cabfSNoralf Trønnes .update = gm12u320_pipe_update, 61477b8cabfSNoralf Trønnes }; 61577b8cabfSNoralf Trønnes 61677b8cabfSNoralf Trønnes static const uint32_t gm12u320_pipe_formats[] = { 61777b8cabfSNoralf Trønnes DRM_FORMAT_XRGB8888, 61877b8cabfSNoralf Trønnes }; 61977b8cabfSNoralf Trønnes 62077b8cabfSNoralf Trønnes static const uint64_t gm12u320_pipe_modifiers[] = { 62177b8cabfSNoralf Trønnes DRM_FORMAT_MOD_LINEAR, 62277b8cabfSNoralf Trønnes DRM_FORMAT_MOD_INVALID 62377b8cabfSNoralf Trønnes }; 62477b8cabfSNoralf Trønnes 62577b8cabfSNoralf Trønnes static void gm12u320_driver_release(struct drm_device *dev) 62677b8cabfSNoralf Trønnes { 62777b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320 = dev->dev_private; 62877b8cabfSNoralf Trønnes 62977b8cabfSNoralf Trønnes gm12u320_usb_free(gm12u320); 63077b8cabfSNoralf Trønnes } 63177b8cabfSNoralf Trønnes 632eee9a2e0SGerd Hoffmann DEFINE_DRM_GEM_FOPS(gm12u320_fops); 63377b8cabfSNoralf Trønnes 63477b8cabfSNoralf Trønnes static struct drm_driver gm12u320_drm_driver = { 63577b8cabfSNoralf Trønnes .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, 63677b8cabfSNoralf Trønnes 63777b8cabfSNoralf Trønnes .name = DRIVER_NAME, 63877b8cabfSNoralf Trønnes .desc = DRIVER_DESC, 63977b8cabfSNoralf Trønnes .date = DRIVER_DATE, 64077b8cabfSNoralf Trønnes .major = DRIVER_MAJOR, 64177b8cabfSNoralf Trønnes .minor = DRIVER_MINOR, 64277b8cabfSNoralf Trønnes 64377b8cabfSNoralf Trønnes .release = gm12u320_driver_release, 64477b8cabfSNoralf Trønnes .fops = &gm12u320_fops, 64577b8cabfSNoralf Trønnes DRM_GEM_SHMEM_DRIVER_OPS, 64677b8cabfSNoralf Trønnes }; 64777b8cabfSNoralf Trønnes 64877b8cabfSNoralf Trønnes static const struct drm_mode_config_funcs gm12u320_mode_config_funcs = { 64977b8cabfSNoralf Trønnes .fb_create = drm_gem_fb_create_with_dirty, 65077b8cabfSNoralf Trønnes .atomic_check = drm_atomic_helper_check, 65177b8cabfSNoralf Trønnes .atomic_commit = drm_atomic_helper_commit, 65277b8cabfSNoralf Trønnes }; 65377b8cabfSNoralf Trønnes 65477b8cabfSNoralf Trønnes static int gm12u320_usb_probe(struct usb_interface *interface, 65577b8cabfSNoralf Trønnes const struct usb_device_id *id) 65677b8cabfSNoralf Trønnes { 65777b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320; 65877b8cabfSNoralf Trønnes struct drm_device *dev; 65977b8cabfSNoralf Trønnes int ret; 66077b8cabfSNoralf Trønnes 66177b8cabfSNoralf Trønnes /* 66277b8cabfSNoralf Trønnes * The gm12u320 presents itself to the system as 2 usb mass-storage 66377b8cabfSNoralf Trønnes * interfaces, we only care about / need the first one. 66477b8cabfSNoralf Trønnes */ 66577b8cabfSNoralf Trønnes if (interface->cur_altsetting->desc.bInterfaceNumber != 0) 66677b8cabfSNoralf Trønnes return -ENODEV; 66777b8cabfSNoralf Trønnes 66877b8cabfSNoralf Trønnes gm12u320 = kzalloc(sizeof(*gm12u320), GFP_KERNEL); 66977b8cabfSNoralf Trønnes if (gm12u320 == NULL) 67077b8cabfSNoralf Trønnes return -ENOMEM; 67177b8cabfSNoralf Trønnes 67277b8cabfSNoralf Trønnes gm12u320->udev = interface_to_usbdev(interface); 67377b8cabfSNoralf Trønnes INIT_WORK(&gm12u320->fb_update.work, gm12u320_fb_update_work); 67477b8cabfSNoralf Trønnes mutex_init(&gm12u320->fb_update.lock); 67577b8cabfSNoralf Trønnes init_waitqueue_head(&gm12u320->fb_update.waitq); 67677b8cabfSNoralf Trønnes 67777b8cabfSNoralf Trønnes dev = &gm12u320->dev; 678993f5b19SDaniel Vetter ret = devm_drm_dev_init(&interface->dev, dev, &gm12u320_drm_driver); 67977b8cabfSNoralf Trønnes if (ret) { 68077b8cabfSNoralf Trønnes kfree(gm12u320); 68177b8cabfSNoralf Trønnes return ret; 68277b8cabfSNoralf Trønnes } 68377b8cabfSNoralf Trønnes dev->dev_private = gm12u320; 684b6731025SDaniel Vetter drmm_add_final_kfree(dev, gm12u320); 68577b8cabfSNoralf Trønnes 68608373edcSDaniel Vetter ret = drmm_mode_config_init(dev); 68708373edcSDaniel Vetter if (ret) 688993f5b19SDaniel Vetter return ret; 68908373edcSDaniel Vetter 69077b8cabfSNoralf Trønnes dev->mode_config.min_width = GM12U320_USER_WIDTH; 69177b8cabfSNoralf Trønnes dev->mode_config.max_width = GM12U320_USER_WIDTH; 69277b8cabfSNoralf Trønnes dev->mode_config.min_height = GM12U320_HEIGHT; 69377b8cabfSNoralf Trønnes dev->mode_config.max_height = GM12U320_HEIGHT; 69477b8cabfSNoralf Trønnes dev->mode_config.funcs = &gm12u320_mode_config_funcs; 69577b8cabfSNoralf Trønnes 69677b8cabfSNoralf Trønnes ret = gm12u320_usb_alloc(gm12u320); 69777b8cabfSNoralf Trønnes if (ret) 698993f5b19SDaniel Vetter return ret; 69977b8cabfSNoralf Trønnes 70077b8cabfSNoralf Trønnes ret = gm12u320_set_ecomode(gm12u320); 70177b8cabfSNoralf Trønnes if (ret) 702993f5b19SDaniel Vetter return ret; 70377b8cabfSNoralf Trønnes 70477b8cabfSNoralf Trønnes ret = gm12u320_conn_init(gm12u320); 70577b8cabfSNoralf Trønnes if (ret) 706993f5b19SDaniel Vetter return ret; 70777b8cabfSNoralf Trønnes 70877b8cabfSNoralf Trønnes ret = drm_simple_display_pipe_init(&gm12u320->dev, 70977b8cabfSNoralf Trønnes &gm12u320->pipe, 71077b8cabfSNoralf Trønnes &gm12u320_pipe_funcs, 71177b8cabfSNoralf Trønnes gm12u320_pipe_formats, 71277b8cabfSNoralf Trønnes ARRAY_SIZE(gm12u320_pipe_formats), 71377b8cabfSNoralf Trønnes gm12u320_pipe_modifiers, 71477b8cabfSNoralf Trønnes &gm12u320->conn); 71577b8cabfSNoralf Trønnes if (ret) 716993f5b19SDaniel Vetter return ret; 71777b8cabfSNoralf Trønnes 71877b8cabfSNoralf Trønnes drm_mode_config_reset(dev); 71977b8cabfSNoralf Trønnes 72077b8cabfSNoralf Trønnes usb_set_intfdata(interface, dev); 72177b8cabfSNoralf Trønnes ret = drm_dev_register(dev, 0); 72277b8cabfSNoralf Trønnes if (ret) 723993f5b19SDaniel Vetter return ret; 72477b8cabfSNoralf Trønnes 7258515090cSHans de Goede drm_fbdev_generic_setup(dev, 0); 72677b8cabfSNoralf Trønnes 72777b8cabfSNoralf Trønnes return 0; 72877b8cabfSNoralf Trønnes } 72977b8cabfSNoralf Trønnes 73077b8cabfSNoralf Trønnes static void gm12u320_usb_disconnect(struct usb_interface *interface) 73177b8cabfSNoralf Trønnes { 73277b8cabfSNoralf Trønnes struct drm_device *dev = usb_get_intfdata(interface); 73377b8cabfSNoralf Trønnes 73477b8cabfSNoralf Trønnes drm_dev_unplug(dev); 735*7ef64ed1SDaniel Vetter drm_atomic_helper_shutdown(dev); 73677b8cabfSNoralf Trønnes } 73777b8cabfSNoralf Trønnes 7388515090cSHans de Goede static __maybe_unused int gm12u320_suspend(struct usb_interface *interface, 73977b8cabfSNoralf Trønnes pm_message_t message) 74077b8cabfSNoralf Trønnes { 74177b8cabfSNoralf Trønnes struct drm_device *dev = usb_get_intfdata(interface); 74277b8cabfSNoralf Trønnes 743*7ef64ed1SDaniel Vetter return drm_mode_config_helper_suspend(dev); 74477b8cabfSNoralf Trønnes } 74577b8cabfSNoralf Trønnes 7468515090cSHans de Goede static __maybe_unused int gm12u320_resume(struct usb_interface *interface) 74777b8cabfSNoralf Trønnes { 74877b8cabfSNoralf Trønnes struct drm_device *dev = usb_get_intfdata(interface); 74977b8cabfSNoralf Trønnes struct gm12u320_device *gm12u320 = dev->dev_private; 75077b8cabfSNoralf Trønnes 75177b8cabfSNoralf Trønnes gm12u320_set_ecomode(gm12u320); 75277b8cabfSNoralf Trønnes 753*7ef64ed1SDaniel Vetter return drm_mode_config_helper_resume(dev); 75477b8cabfSNoralf Trønnes } 75577b8cabfSNoralf Trønnes 75677b8cabfSNoralf Trønnes static const struct usb_device_id id_table[] = { 75777b8cabfSNoralf Trønnes { USB_DEVICE(0x1de1, 0xc102) }, 75877b8cabfSNoralf Trønnes {}, 75977b8cabfSNoralf Trønnes }; 76077b8cabfSNoralf Trønnes MODULE_DEVICE_TABLE(usb, id_table); 76177b8cabfSNoralf Trønnes 76277b8cabfSNoralf Trønnes static struct usb_driver gm12u320_usb_driver = { 76377b8cabfSNoralf Trønnes .name = "gm12u320", 76477b8cabfSNoralf Trønnes .probe = gm12u320_usb_probe, 76577b8cabfSNoralf Trønnes .disconnect = gm12u320_usb_disconnect, 76677b8cabfSNoralf Trønnes .id_table = id_table, 76777b8cabfSNoralf Trønnes #ifdef CONFIG_PM 76877b8cabfSNoralf Trønnes .suspend = gm12u320_suspend, 76977b8cabfSNoralf Trønnes .resume = gm12u320_resume, 77077b8cabfSNoralf Trønnes .reset_resume = gm12u320_resume, 77177b8cabfSNoralf Trønnes #endif 77277b8cabfSNoralf Trønnes }; 77377b8cabfSNoralf Trønnes 77477b8cabfSNoralf Trønnes module_usb_driver(gm12u320_usb_driver); 77577b8cabfSNoralf Trønnes MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 77677b8cabfSNoralf Trønnes MODULE_LICENSE("GPL"); 777