162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * based in parts on udlfb.c: 662306a36Sopenharmony_ci * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it> 762306a36Sopenharmony_ci * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com> 862306a36Sopenharmony_ci * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/bitfield.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1462306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1562306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h> 1662306a36Sopenharmony_ci#include <drm/drm_damage_helper.h> 1762306a36Sopenharmony_ci#include <drm/drm_drv.h> 1862306a36Sopenharmony_ci#include <drm/drm_edid.h> 1962306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2062306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 2162306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 2262306a36Sopenharmony_ci#include <drm/drm_gem_shmem_helper.h> 2362306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h> 2462306a36Sopenharmony_ci#include <drm/drm_plane_helper.h> 2562306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2662306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "udl_drv.h" 2962306a36Sopenharmony_ci#include "udl_proto.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * All DisplayLink bulk operations start with 0xaf (UDL_MSG_BULK), followed by 3362306a36Sopenharmony_ci * a specific command code. All operations are written to a command buffer, which 3462306a36Sopenharmony_ci * the driver sends to the device. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistatic char *udl_set_register(char *buf, u8 reg, u8 val) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci *buf++ = UDL_MSG_BULK; 3962306a36Sopenharmony_ci *buf++ = UDL_CMD_WRITEREG; 4062306a36Sopenharmony_ci *buf++ = reg; 4162306a36Sopenharmony_ci *buf++ = val; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return buf; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic char *udl_vidreg_lock(char *buf) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci return udl_set_register(buf, UDL_REG_VIDREG, UDL_VIDREG_LOCK); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic char *udl_vidreg_unlock(char *buf) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci return udl_set_register(buf, UDL_REG_VIDREG, UDL_VIDREG_UNLOCK); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic char *udl_set_blank_mode(char *buf, u8 mode) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci return udl_set_register(buf, UDL_REG_BLANKMODE, mode); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic char *udl_set_color_depth(char *buf, u8 selection) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return udl_set_register(buf, UDL_REG_COLORDEPTH, selection); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic char *udl_set_base16bpp(char *buf, u32 base) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci /* the base pointer is 24 bits wide, 0x20 is hi byte. */ 6962306a36Sopenharmony_ci u8 reg20 = FIELD_GET(UDL_BASE_ADDR2_MASK, base); 7062306a36Sopenharmony_ci u8 reg21 = FIELD_GET(UDL_BASE_ADDR1_MASK, base); 7162306a36Sopenharmony_ci u8 reg22 = FIELD_GET(UDL_BASE_ADDR0_MASK, base); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci buf = udl_set_register(buf, UDL_REG_BASE16BPP_ADDR2, reg20); 7462306a36Sopenharmony_ci buf = udl_set_register(buf, UDL_REG_BASE16BPP_ADDR1, reg21); 7562306a36Sopenharmony_ci buf = udl_set_register(buf, UDL_REG_BASE16BPP_ADDR0, reg22); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return buf; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * DisplayLink HW has separate 16bpp and 8bpp framebuffers. 8262306a36Sopenharmony_ci * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic char *udl_set_base8bpp(char *buf, u32 base) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci /* the base pointer is 24 bits wide, 0x26 is hi byte. */ 8762306a36Sopenharmony_ci u8 reg26 = FIELD_GET(UDL_BASE_ADDR2_MASK, base); 8862306a36Sopenharmony_ci u8 reg27 = FIELD_GET(UDL_BASE_ADDR1_MASK, base); 8962306a36Sopenharmony_ci u8 reg28 = FIELD_GET(UDL_BASE_ADDR0_MASK, base); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci buf = udl_set_register(buf, UDL_REG_BASE8BPP_ADDR2, reg26); 9262306a36Sopenharmony_ci buf = udl_set_register(buf, UDL_REG_BASE8BPP_ADDR1, reg27); 9362306a36Sopenharmony_ci buf = udl_set_register(buf, UDL_REG_BASE8BPP_ADDR0, reg28); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return buf; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic char *udl_set_register_16(char *wrptr, u8 reg, u16 value) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci wrptr = udl_set_register(wrptr, reg, value >> 8); 10162306a36Sopenharmony_ci return udl_set_register(wrptr, reg+1, value); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * This is kind of weird because the controller takes some 10662306a36Sopenharmony_ci * register values in a different byte order than other registers. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_cistatic char *udl_set_register_16be(char *wrptr, u8 reg, u16 value) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci wrptr = udl_set_register(wrptr, reg, value); 11162306a36Sopenharmony_ci return udl_set_register(wrptr, reg+1, value >> 8); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * LFSR is linear feedback shift register. The reason we have this is 11662306a36Sopenharmony_ci * because the display controller needs to minimize the clock depth of 11762306a36Sopenharmony_ci * various counters used in the display path. So this code reverses the 11862306a36Sopenharmony_ci * provided value into the lfsr16 value by counting backwards to get 11962306a36Sopenharmony_ci * the value that needs to be set in the hardware comparator to get the 12062306a36Sopenharmony_ci * same actual count. This makes sense once you read above a couple of 12162306a36Sopenharmony_ci * times and think about it from a hardware perspective. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_cistatic u16 udl_lfsr16(u16 actual_count) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */ 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci while (actual_count--) { 12862306a36Sopenharmony_ci lv = ((lv << 1) | 12962306a36Sopenharmony_ci (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1)) 13062306a36Sopenharmony_ci & 0xFFFF; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return (u16) lv; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * This does LFSR conversion on the value that is to be written. 13862306a36Sopenharmony_ci * See LFSR explanation above for more detail. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic char *udl_set_register_lfsr16(char *wrptr, u8 reg, u16 value) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci return udl_set_register_16(wrptr, reg, udl_lfsr16(value)); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* 14662306a36Sopenharmony_ci * Takes a DRM display mode and converts it into the DisplayLink 14762306a36Sopenharmony_ci * equivalent register commands. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistatic char *udl_set_display_mode(char *buf, struct drm_display_mode *mode) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci u16 reg01 = mode->crtc_htotal - mode->crtc_hsync_start; 15262306a36Sopenharmony_ci u16 reg03 = reg01 + mode->crtc_hdisplay; 15362306a36Sopenharmony_ci u16 reg05 = mode->crtc_vtotal - mode->crtc_vsync_start; 15462306a36Sopenharmony_ci u16 reg07 = reg05 + mode->crtc_vdisplay; 15562306a36Sopenharmony_ci u16 reg09 = mode->crtc_htotal - 1; 15662306a36Sopenharmony_ci u16 reg0b = 1; /* libdlo hardcodes hsync start to 1 */ 15762306a36Sopenharmony_ci u16 reg0d = mode->crtc_hsync_end - mode->crtc_hsync_start + 1; 15862306a36Sopenharmony_ci u16 reg0f = mode->hdisplay; 15962306a36Sopenharmony_ci u16 reg11 = mode->crtc_vtotal; 16062306a36Sopenharmony_ci u16 reg13 = 0; /* libdlo hardcodes vsync start to 0 */ 16162306a36Sopenharmony_ci u16 reg15 = mode->crtc_vsync_end - mode->crtc_vsync_start; 16262306a36Sopenharmony_ci u16 reg17 = mode->crtc_vdisplay; 16362306a36Sopenharmony_ci u16 reg1b = mode->clock / 5; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_XDISPLAYSTART, reg01); 16662306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_XDISPLAYEND, reg03); 16762306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_YDISPLAYSTART, reg05); 16862306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_YDISPLAYEND, reg07); 16962306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_XENDCOUNT, reg09); 17062306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_HSYNCSTART, reg0b); 17162306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_HSYNCEND, reg0d); 17262306a36Sopenharmony_ci buf = udl_set_register_16(buf, UDL_REG_HPIXELS, reg0f); 17362306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_YENDCOUNT, reg11); 17462306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_VSYNCSTART, reg13); 17562306a36Sopenharmony_ci buf = udl_set_register_lfsr16(buf, UDL_REG_VSYNCEND, reg15); 17662306a36Sopenharmony_ci buf = udl_set_register_16(buf, UDL_REG_VPIXELS, reg17); 17762306a36Sopenharmony_ci buf = udl_set_register_16be(buf, UDL_REG_PIXELCLOCK5KHZ, reg1b); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return buf; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic char *udl_dummy_render(char *wrptr) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci *wrptr++ = UDL_MSG_BULK; 18562306a36Sopenharmony_ci *wrptr++ = UDL_CMD_WRITECOPY16; 18662306a36Sopenharmony_ci *wrptr++ = 0x00; /* from addr */ 18762306a36Sopenharmony_ci *wrptr++ = 0x00; 18862306a36Sopenharmony_ci *wrptr++ = 0x00; 18962306a36Sopenharmony_ci *wrptr++ = 0x01; /* one pixel */ 19062306a36Sopenharmony_ci *wrptr++ = 0x00; /* to address */ 19162306a36Sopenharmony_ci *wrptr++ = 0x00; 19262306a36Sopenharmony_ci *wrptr++ = 0x00; 19362306a36Sopenharmony_ci return wrptr; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic long udl_log_cpp(unsigned int cpp) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci if (WARN_ON(!is_power_of_2(cpp))) 19962306a36Sopenharmony_ci return -EINVAL; 20062306a36Sopenharmony_ci return __ffs(cpp); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int udl_handle_damage(struct drm_framebuffer *fb, 20462306a36Sopenharmony_ci const struct iosys_map *map, 20562306a36Sopenharmony_ci const struct drm_rect *clip) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct drm_device *dev = fb->dev; 20862306a36Sopenharmony_ci void *vaddr = map->vaddr; /* TODO: Use mapping abstraction properly */ 20962306a36Sopenharmony_ci int i, ret; 21062306a36Sopenharmony_ci char *cmd; 21162306a36Sopenharmony_ci struct urb *urb; 21262306a36Sopenharmony_ci int log_bpp; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci ret = udl_log_cpp(fb->format->cpp[0]); 21562306a36Sopenharmony_ci if (ret < 0) 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci log_bpp = ret; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci urb = udl_get_urb(dev); 22062306a36Sopenharmony_ci if (!urb) 22162306a36Sopenharmony_ci return -ENOMEM; 22262306a36Sopenharmony_ci cmd = urb->transfer_buffer; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci for (i = clip->y1; i < clip->y2; i++) { 22562306a36Sopenharmony_ci const int line_offset = fb->pitches[0] * i; 22662306a36Sopenharmony_ci const int byte_offset = line_offset + (clip->x1 << log_bpp); 22762306a36Sopenharmony_ci const int dev_byte_offset = (fb->width * i + clip->x1) << log_bpp; 22862306a36Sopenharmony_ci const int byte_width = drm_rect_width(clip) << log_bpp; 22962306a36Sopenharmony_ci ret = udl_render_hline(dev, log_bpp, &urb, (char *)vaddr, 23062306a36Sopenharmony_ci &cmd, byte_offset, dev_byte_offset, 23162306a36Sopenharmony_ci byte_width); 23262306a36Sopenharmony_ci if (ret) 23362306a36Sopenharmony_ci return ret; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (cmd > (char *)urb->transfer_buffer) { 23762306a36Sopenharmony_ci /* Send partial buffer remaining before exiting */ 23862306a36Sopenharmony_ci int len; 23962306a36Sopenharmony_ci if (cmd < (char *)urb->transfer_buffer + urb->transfer_buffer_length) 24062306a36Sopenharmony_ci *cmd++ = UDL_MSG_BULK; 24162306a36Sopenharmony_ci len = cmd - (char *)urb->transfer_buffer; 24262306a36Sopenharmony_ci ret = udl_submit_urb(dev, urb, len); 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci udl_urb_completion(urb); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return 0; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* 25162306a36Sopenharmony_ci * Primary plane 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic const uint32_t udl_primary_plane_formats[] = { 25562306a36Sopenharmony_ci DRM_FORMAT_RGB565, 25662306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic const uint64_t udl_primary_plane_fmtmods[] = { 26062306a36Sopenharmony_ci DRM_FORMAT_MOD_LINEAR, 26162306a36Sopenharmony_ci DRM_FORMAT_MOD_INVALID 26262306a36Sopenharmony_ci}; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void udl_primary_plane_helper_atomic_update(struct drm_plane *plane, 26562306a36Sopenharmony_ci struct drm_atomic_state *state) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct drm_device *dev = plane->dev; 26862306a36Sopenharmony_ci struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); 26962306a36Sopenharmony_ci struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); 27062306a36Sopenharmony_ci struct drm_framebuffer *fb = plane_state->fb; 27162306a36Sopenharmony_ci struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); 27262306a36Sopenharmony_ci struct drm_atomic_helper_damage_iter iter; 27362306a36Sopenharmony_ci struct drm_rect damage; 27462306a36Sopenharmony_ci int ret, idx; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (!fb) 27762306a36Sopenharmony_ci return; /* no framebuffer; plane is disabled */ 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); 28062306a36Sopenharmony_ci if (ret) 28162306a36Sopenharmony_ci return; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (!drm_dev_enter(dev, &idx)) 28462306a36Sopenharmony_ci goto out_drm_gem_fb_end_cpu_access; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state); 28762306a36Sopenharmony_ci drm_atomic_for_each_plane_damage(&iter, &damage) { 28862306a36Sopenharmony_ci udl_handle_damage(fb, &shadow_plane_state->data[0], &damage); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci drm_dev_exit(idx); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ciout_drm_gem_fb_end_cpu_access: 29462306a36Sopenharmony_ci drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs udl_primary_plane_helper_funcs = { 29862306a36Sopenharmony_ci DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, 29962306a36Sopenharmony_ci .atomic_check = drm_plane_helper_atomic_check, 30062306a36Sopenharmony_ci .atomic_update = udl_primary_plane_helper_atomic_update, 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic const struct drm_plane_funcs udl_primary_plane_funcs = { 30462306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 30562306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 30662306a36Sopenharmony_ci .destroy = drm_plane_cleanup, 30762306a36Sopenharmony_ci DRM_GEM_SHADOW_PLANE_FUNCS, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* 31162306a36Sopenharmony_ci * CRTC 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic void udl_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 31762306a36Sopenharmony_ci struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 31862306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc_state->mode; 31962306a36Sopenharmony_ci struct urb *urb; 32062306a36Sopenharmony_ci char *buf; 32162306a36Sopenharmony_ci int idx; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!drm_dev_enter(dev, &idx)) 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci urb = udl_get_urb(dev); 32762306a36Sopenharmony_ci if (!urb) 32862306a36Sopenharmony_ci goto out; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci buf = (char *)urb->transfer_buffer; 33162306a36Sopenharmony_ci buf = udl_vidreg_lock(buf); 33262306a36Sopenharmony_ci buf = udl_set_color_depth(buf, UDL_COLORDEPTH_16BPP); 33362306a36Sopenharmony_ci /* set base for 16bpp segment to 0 */ 33462306a36Sopenharmony_ci buf = udl_set_base16bpp(buf, 0); 33562306a36Sopenharmony_ci /* set base for 8bpp segment to end of fb */ 33662306a36Sopenharmony_ci buf = udl_set_base8bpp(buf, 2 * mode->vdisplay * mode->hdisplay); 33762306a36Sopenharmony_ci buf = udl_set_display_mode(buf, mode); 33862306a36Sopenharmony_ci buf = udl_set_blank_mode(buf, UDL_BLANKMODE_ON); 33962306a36Sopenharmony_ci buf = udl_vidreg_unlock(buf); 34062306a36Sopenharmony_ci buf = udl_dummy_render(buf); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci udl_submit_urb(dev, urb, buf - (char *)urb->transfer_buffer); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciout: 34562306a36Sopenharmony_ci drm_dev_exit(idx); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void udl_crtc_helper_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 35162306a36Sopenharmony_ci struct urb *urb; 35262306a36Sopenharmony_ci char *buf; 35362306a36Sopenharmony_ci int idx; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!drm_dev_enter(dev, &idx)) 35662306a36Sopenharmony_ci return; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci urb = udl_get_urb(dev); 35962306a36Sopenharmony_ci if (!urb) 36062306a36Sopenharmony_ci goto out; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci buf = (char *)urb->transfer_buffer; 36362306a36Sopenharmony_ci buf = udl_vidreg_lock(buf); 36462306a36Sopenharmony_ci buf = udl_set_blank_mode(buf, UDL_BLANKMODE_POWERDOWN); 36562306a36Sopenharmony_ci buf = udl_vidreg_unlock(buf); 36662306a36Sopenharmony_ci buf = udl_dummy_render(buf); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci udl_submit_urb(dev, urb, buf - (char *)urb->transfer_buffer); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ciout: 37162306a36Sopenharmony_ci drm_dev_exit(idx); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs udl_crtc_helper_funcs = { 37562306a36Sopenharmony_ci .atomic_check = drm_crtc_helper_atomic_check, 37662306a36Sopenharmony_ci .atomic_enable = udl_crtc_helper_atomic_enable, 37762306a36Sopenharmony_ci .atomic_disable = udl_crtc_helper_atomic_disable, 37862306a36Sopenharmony_ci}; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic const struct drm_crtc_funcs udl_crtc_funcs = { 38162306a36Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 38262306a36Sopenharmony_ci .destroy = drm_crtc_cleanup, 38362306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 38462306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 38562306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 38662306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 38762306a36Sopenharmony_ci}; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* 39062306a36Sopenharmony_ci * Encoder 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic const struct drm_encoder_funcs udl_encoder_funcs = { 39462306a36Sopenharmony_ci .destroy = drm_encoder_cleanup, 39562306a36Sopenharmony_ci}; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* 39862306a36Sopenharmony_ci * Connector 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int udl_connector_helper_get_modes(struct drm_connector *connector) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct udl_connector *udl_connector = to_udl_connector(connector); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci drm_connector_update_edid_property(connector, udl_connector->edid); 40662306a36Sopenharmony_ci if (udl_connector->edid) 40762306a36Sopenharmony_ci return drm_add_edid_modes(connector, udl_connector->edid); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs udl_connector_helper_funcs = { 41362306a36Sopenharmony_ci .get_modes = udl_connector_helper_get_modes, 41462306a36Sopenharmony_ci}; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic int udl_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct udl_device *udl = data; 41962306a36Sopenharmony_ci struct drm_device *dev = &udl->drm; 42062306a36Sopenharmony_ci struct usb_device *udev = udl_to_usb_device(udl); 42162306a36Sopenharmony_ci u8 *read_buff; 42262306a36Sopenharmony_ci int ret; 42362306a36Sopenharmony_ci size_t i; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci read_buff = kmalloc(2, GFP_KERNEL); 42662306a36Sopenharmony_ci if (!read_buff) 42762306a36Sopenharmony_ci return -ENOMEM; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci for (i = 0; i < len; i++) { 43062306a36Sopenharmony_ci int bval = (i + block * EDID_LENGTH) << 8; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 43362306a36Sopenharmony_ci 0x02, (0x80 | (0x02 << 5)), bval, 43462306a36Sopenharmony_ci 0xA1, read_buff, 2, USB_CTRL_GET_TIMEOUT); 43562306a36Sopenharmony_ci if (ret < 0) { 43662306a36Sopenharmony_ci drm_err(dev, "Read EDID byte %zu failed err %x\n", i, ret); 43762306a36Sopenharmony_ci goto err_kfree; 43862306a36Sopenharmony_ci } else if (ret < 1) { 43962306a36Sopenharmony_ci ret = -EIO; 44062306a36Sopenharmony_ci drm_err(dev, "Read EDID byte %zu failed\n", i); 44162306a36Sopenharmony_ci goto err_kfree; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci buf[i] = read_buff[1]; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci kfree(read_buff); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cierr_kfree: 45262306a36Sopenharmony_ci kfree(read_buff); 45362306a36Sopenharmony_ci return ret; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic enum drm_connector_status udl_connector_detect(struct drm_connector *connector, bool force) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 45962306a36Sopenharmony_ci struct udl_device *udl = to_udl(dev); 46062306a36Sopenharmony_ci struct udl_connector *udl_connector = to_udl_connector(connector); 46162306a36Sopenharmony_ci enum drm_connector_status status = connector_status_disconnected; 46262306a36Sopenharmony_ci int idx; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* cleanup previous EDID */ 46562306a36Sopenharmony_ci kfree(udl_connector->edid); 46662306a36Sopenharmony_ci udl_connector->edid = NULL; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (!drm_dev_enter(dev, &idx)) 46962306a36Sopenharmony_ci return connector_status_disconnected; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci udl_connector->edid = drm_do_get_edid(connector, udl_get_edid_block, udl); 47262306a36Sopenharmony_ci if (udl_connector->edid) 47362306a36Sopenharmony_ci status = connector_status_connected; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci drm_dev_exit(idx); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return status; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void udl_connector_destroy(struct drm_connector *connector) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct udl_connector *udl_connector = to_udl_connector(connector); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci drm_connector_cleanup(connector); 48562306a36Sopenharmony_ci kfree(udl_connector->edid); 48662306a36Sopenharmony_ci kfree(udl_connector); 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic const struct drm_connector_funcs udl_connector_funcs = { 49062306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 49162306a36Sopenharmony_ci .detect = udl_connector_detect, 49262306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 49362306a36Sopenharmony_ci .destroy = udl_connector_destroy, 49462306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 49562306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 49662306a36Sopenharmony_ci}; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistruct drm_connector *udl_connector_init(struct drm_device *dev) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct udl_connector *udl_connector; 50162306a36Sopenharmony_ci struct drm_connector *connector; 50262306a36Sopenharmony_ci int ret; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci udl_connector = kzalloc(sizeof(*udl_connector), GFP_KERNEL); 50562306a36Sopenharmony_ci if (!udl_connector) 50662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci connector = &udl_connector->connector; 50962306a36Sopenharmony_ci ret = drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_VGA); 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci goto err_kfree; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci drm_connector_helper_add(connector, &udl_connector_helper_funcs); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD | 51662306a36Sopenharmony_ci DRM_CONNECTOR_POLL_CONNECT | 51762306a36Sopenharmony_ci DRM_CONNECTOR_POLL_DISCONNECT; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return connector; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cierr_kfree: 52262306a36Sopenharmony_ci kfree(udl_connector); 52362306a36Sopenharmony_ci return ERR_PTR(ret); 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/* 52762306a36Sopenharmony_ci * Modesetting 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic enum drm_mode_status udl_mode_config_mode_valid(struct drm_device *dev, 53162306a36Sopenharmony_ci const struct drm_display_mode *mode) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct udl_device *udl = to_udl(dev); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (udl->sku_pixel_limit) { 53662306a36Sopenharmony_ci if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit) 53762306a36Sopenharmony_ci return MODE_MEM; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return MODE_OK; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic const struct drm_mode_config_funcs udl_mode_config_funcs = { 54462306a36Sopenharmony_ci .fb_create = drm_gem_fb_create_with_dirty, 54562306a36Sopenharmony_ci .mode_valid = udl_mode_config_mode_valid, 54662306a36Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 54762306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 54862306a36Sopenharmony_ci}; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ciint udl_modeset_init(struct drm_device *dev) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct udl_device *udl = to_udl(dev); 55362306a36Sopenharmony_ci struct drm_plane *primary_plane; 55462306a36Sopenharmony_ci struct drm_crtc *crtc; 55562306a36Sopenharmony_ci struct drm_encoder *encoder; 55662306a36Sopenharmony_ci struct drm_connector *connector; 55762306a36Sopenharmony_ci int ret; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci ret = drmm_mode_config_init(dev); 56062306a36Sopenharmony_ci if (ret) 56162306a36Sopenharmony_ci return ret; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci dev->mode_config.min_width = 640; 56462306a36Sopenharmony_ci dev->mode_config.min_height = 480; 56562306a36Sopenharmony_ci dev->mode_config.max_width = 2048; 56662306a36Sopenharmony_ci dev->mode_config.max_height = 2048; 56762306a36Sopenharmony_ci dev->mode_config.preferred_depth = 16; 56862306a36Sopenharmony_ci dev->mode_config.funcs = &udl_mode_config_funcs; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci primary_plane = &udl->primary_plane; 57162306a36Sopenharmony_ci ret = drm_universal_plane_init(dev, primary_plane, 0, 57262306a36Sopenharmony_ci &udl_primary_plane_funcs, 57362306a36Sopenharmony_ci udl_primary_plane_formats, 57462306a36Sopenharmony_ci ARRAY_SIZE(udl_primary_plane_formats), 57562306a36Sopenharmony_ci udl_primary_plane_fmtmods, 57662306a36Sopenharmony_ci DRM_PLANE_TYPE_PRIMARY, NULL); 57762306a36Sopenharmony_ci if (ret) 57862306a36Sopenharmony_ci return ret; 57962306a36Sopenharmony_ci drm_plane_helper_add(primary_plane, &udl_primary_plane_helper_funcs); 58062306a36Sopenharmony_ci drm_plane_enable_fb_damage_clips(primary_plane); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci crtc = &udl->crtc; 58362306a36Sopenharmony_ci ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL, 58462306a36Sopenharmony_ci &udl_crtc_funcs, NULL); 58562306a36Sopenharmony_ci if (ret) 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci drm_crtc_helper_add(crtc, &udl_crtc_helper_funcs); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci encoder = &udl->encoder; 59062306a36Sopenharmony_ci ret = drm_encoder_init(dev, encoder, &udl_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL); 59162306a36Sopenharmony_ci if (ret) 59262306a36Sopenharmony_ci return ret; 59362306a36Sopenharmony_ci encoder->possible_crtcs = drm_crtc_mask(crtc); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci connector = udl_connector_init(dev); 59662306a36Sopenharmony_ci if (IS_ERR(connector)) 59762306a36Sopenharmony_ci return PTR_ERR(connector); 59862306a36Sopenharmony_ci ret = drm_connector_attach_encoder(connector, encoder); 59962306a36Sopenharmony_ci if (ret) 60062306a36Sopenharmony_ci return ret; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci drm_mode_config_reset(dev); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 606