cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

udl_modeset.c (13335B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2012 Red Hat
      4 *
      5 * based in parts on udlfb.c:
      6 * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
      7 * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
      8 * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
      9 */
     10
     11#include <drm/drm_atomic_helper.h>
     12#include <drm/drm_crtc_helper.h>
     13#include <drm/drm_damage_helper.h>
     14#include <drm/drm_fourcc.h>
     15#include <drm/drm_gem_atomic_helper.h>
     16#include <drm/drm_gem_framebuffer_helper.h>
     17#include <drm/drm_gem_shmem_helper.h>
     18#include <drm/drm_modeset_helper_vtables.h>
     19#include <drm/drm_vblank.h>
     20
     21#include "udl_drv.h"
     22
     23#define UDL_COLOR_DEPTH_16BPP	0
     24
     25/*
     26 * All DisplayLink bulk operations start with 0xAF, followed by specific code
     27 * All operations are written to buffers which then later get sent to device
     28 */
     29static char *udl_set_register(char *buf, u8 reg, u8 val)
     30{
     31	*buf++ = 0xAF;
     32	*buf++ = 0x20;
     33	*buf++ = reg;
     34	*buf++ = val;
     35	return buf;
     36}
     37
     38static char *udl_vidreg_lock(char *buf)
     39{
     40	return udl_set_register(buf, 0xFF, 0x00);
     41}
     42
     43static char *udl_vidreg_unlock(char *buf)
     44{
     45	return udl_set_register(buf, 0xFF, 0xFF);
     46}
     47
     48static char *udl_set_blank_mode(char *buf, u8 mode)
     49{
     50	return udl_set_register(buf, UDL_REG_BLANK_MODE, mode);
     51}
     52
     53static char *udl_set_color_depth(char *buf, u8 selection)
     54{
     55	return udl_set_register(buf, 0x00, selection);
     56}
     57
     58static char *udl_set_base16bpp(char *wrptr, u32 base)
     59{
     60	/* the base pointer is 16 bits wide, 0x20 is hi byte. */
     61	wrptr = udl_set_register(wrptr, 0x20, base >> 16);
     62	wrptr = udl_set_register(wrptr, 0x21, base >> 8);
     63	return udl_set_register(wrptr, 0x22, base);
     64}
     65
     66/*
     67 * DisplayLink HW has separate 16bpp and 8bpp framebuffers.
     68 * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
     69 */
     70static char *udl_set_base8bpp(char *wrptr, u32 base)
     71{
     72	wrptr = udl_set_register(wrptr, 0x26, base >> 16);
     73	wrptr = udl_set_register(wrptr, 0x27, base >> 8);
     74	return udl_set_register(wrptr, 0x28, base);
     75}
     76
     77static char *udl_set_register_16(char *wrptr, u8 reg, u16 value)
     78{
     79	wrptr = udl_set_register(wrptr, reg, value >> 8);
     80	return udl_set_register(wrptr, reg+1, value);
     81}
     82
     83/*
     84 * This is kind of weird because the controller takes some
     85 * register values in a different byte order than other registers.
     86 */
     87static char *udl_set_register_16be(char *wrptr, u8 reg, u16 value)
     88{
     89	wrptr = udl_set_register(wrptr, reg, value);
     90	return udl_set_register(wrptr, reg+1, value >> 8);
     91}
     92
     93/*
     94 * LFSR is linear feedback shift register. The reason we have this is
     95 * because the display controller needs to minimize the clock depth of
     96 * various counters used in the display path. So this code reverses the
     97 * provided value into the lfsr16 value by counting backwards to get
     98 * the value that needs to be set in the hardware comparator to get the
     99 * same actual count. This makes sense once you read above a couple of
    100 * times and think about it from a hardware perspective.
    101 */
    102static u16 udl_lfsr16(u16 actual_count)
    103{
    104	u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */
    105
    106	while (actual_count--) {
    107		lv =	 ((lv << 1) |
    108			(((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1))
    109			& 0xFFFF;
    110	}
    111
    112	return (u16) lv;
    113}
    114
    115/*
    116 * This does LFSR conversion on the value that is to be written.
    117 * See LFSR explanation above for more detail.
    118 */
    119static char *udl_set_register_lfsr16(char *wrptr, u8 reg, u16 value)
    120{
    121	return udl_set_register_16(wrptr, reg, udl_lfsr16(value));
    122}
    123
    124/*
    125 * This takes a standard fbdev screeninfo struct and all of its monitor mode
    126 * details and converts them into the DisplayLink equivalent register commands.
    127  ERR(vreg(dev,               0x00, (color_depth == 16) ? 0 : 1));
    128  ERR(vreg_lfsr16(dev,        0x01, xDisplayStart));
    129  ERR(vreg_lfsr16(dev,        0x03, xDisplayEnd));
    130  ERR(vreg_lfsr16(dev,        0x05, yDisplayStart));
    131  ERR(vreg_lfsr16(dev,        0x07, yDisplayEnd));
    132  ERR(vreg_lfsr16(dev,        0x09, xEndCount));
    133  ERR(vreg_lfsr16(dev,        0x0B, hSyncStart));
    134  ERR(vreg_lfsr16(dev,        0x0D, hSyncEnd));
    135  ERR(vreg_big_endian(dev,    0x0F, hPixels));
    136  ERR(vreg_lfsr16(dev,        0x11, yEndCount));
    137  ERR(vreg_lfsr16(dev,        0x13, vSyncStart));
    138  ERR(vreg_lfsr16(dev,        0x15, vSyncEnd));
    139  ERR(vreg_big_endian(dev,    0x17, vPixels));
    140  ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz));
    141
    142  ERR(vreg(dev,               0x1F, 0));
    143
    144  ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
    145 */
    146static char *udl_set_vid_cmds(char *wrptr, struct drm_display_mode *mode)
    147{
    148	u16 xds, yds;
    149	u16 xde, yde;
    150	u16 yec;
    151
    152	/* x display start */
    153	xds = mode->crtc_htotal - mode->crtc_hsync_start;
    154	wrptr = udl_set_register_lfsr16(wrptr, 0x01, xds);
    155	/* x display end */
    156	xde = xds + mode->crtc_hdisplay;
    157	wrptr = udl_set_register_lfsr16(wrptr, 0x03, xde);
    158
    159	/* y display start */
    160	yds = mode->crtc_vtotal - mode->crtc_vsync_start;
    161	wrptr = udl_set_register_lfsr16(wrptr, 0x05, yds);
    162	/* y display end */
    163	yde = yds + mode->crtc_vdisplay;
    164	wrptr = udl_set_register_lfsr16(wrptr, 0x07, yde);
    165
    166	/* x end count is active + blanking - 1 */
    167	wrptr = udl_set_register_lfsr16(wrptr, 0x09,
    168					mode->crtc_htotal - 1);
    169
    170	/* libdlo hardcodes hsync start to 1 */
    171	wrptr = udl_set_register_lfsr16(wrptr, 0x0B, 1);
    172
    173	/* hsync end is width of sync pulse + 1 */
    174	wrptr = udl_set_register_lfsr16(wrptr, 0x0D,
    175					mode->crtc_hsync_end - mode->crtc_hsync_start + 1);
    176
    177	/* hpixels is active pixels */
    178	wrptr = udl_set_register_16(wrptr, 0x0F, mode->hdisplay);
    179
    180	/* yendcount is vertical active + vertical blanking */
    181	yec = mode->crtc_vtotal;
    182	wrptr = udl_set_register_lfsr16(wrptr, 0x11, yec);
    183
    184	/* libdlo hardcodes vsync start to 0 */
    185	wrptr = udl_set_register_lfsr16(wrptr, 0x13, 0);
    186
    187	/* vsync end is width of vsync pulse */
    188	wrptr = udl_set_register_lfsr16(wrptr, 0x15, mode->crtc_vsync_end - mode->crtc_vsync_start);
    189
    190	/* vpixels is active pixels */
    191	wrptr = udl_set_register_16(wrptr, 0x17, mode->crtc_vdisplay);
    192
    193	wrptr = udl_set_register_16be(wrptr, 0x1B,
    194				      mode->clock / 5);
    195
    196	return wrptr;
    197}
    198
    199static char *udl_dummy_render(char *wrptr)
    200{
    201	*wrptr++ = 0xAF;
    202	*wrptr++ = 0x6A; /* copy */
    203	*wrptr++ = 0x00; /* from addr */
    204	*wrptr++ = 0x00;
    205	*wrptr++ = 0x00;
    206	*wrptr++ = 0x01; /* one pixel */
    207	*wrptr++ = 0x00; /* to address */
    208	*wrptr++ = 0x00;
    209	*wrptr++ = 0x00;
    210	return wrptr;
    211}
    212
    213static int udl_crtc_write_mode_to_hw(struct drm_crtc *crtc)
    214{
    215	struct drm_device *dev = crtc->dev;
    216	struct udl_device *udl = to_udl(dev);
    217	struct urb *urb;
    218	char *buf;
    219	int retval;
    220
    221	if (udl->mode_buf_len == 0) {
    222		DRM_ERROR("No mode set\n");
    223		return -EINVAL;
    224	}
    225
    226	urb = udl_get_urb(dev);
    227	if (!urb)
    228		return -ENOMEM;
    229
    230	buf = (char *)urb->transfer_buffer;
    231
    232	memcpy(buf, udl->mode_buf, udl->mode_buf_len);
    233	retval = udl_submit_urb(dev, urb, udl->mode_buf_len);
    234	DRM_DEBUG("write mode info %d\n", udl->mode_buf_len);
    235	return retval;
    236}
    237
    238static long udl_log_cpp(unsigned int cpp)
    239{
    240	if (WARN_ON(!is_power_of_2(cpp)))
    241		return -EINVAL;
    242	return __ffs(cpp);
    243}
    244
    245static int udl_aligned_damage_clip(struct drm_rect *clip, int x, int y,
    246				   int width, int height)
    247{
    248	int x1, x2;
    249
    250	if (WARN_ON_ONCE(x < 0) ||
    251	    WARN_ON_ONCE(y < 0) ||
    252	    WARN_ON_ONCE(width < 0) ||
    253	    WARN_ON_ONCE(height < 0))
    254		return -EINVAL;
    255
    256	x1 = ALIGN_DOWN(x, sizeof(unsigned long));
    257	x2 = ALIGN(width + (x - x1), sizeof(unsigned long)) + x1;
    258
    259	clip->x1 = x1;
    260	clip->y1 = y;
    261	clip->x2 = x2;
    262	clip->y2 = y + height;
    263
    264	return 0;
    265}
    266
    267static int udl_handle_damage(struct drm_framebuffer *fb,
    268			     const struct iosys_map *map,
    269			     int x, int y, int width, int height)
    270{
    271	struct drm_device *dev = fb->dev;
    272	void *vaddr = map->vaddr; /* TODO: Use mapping abstraction properly */
    273	int i, ret;
    274	char *cmd;
    275	struct urb *urb;
    276	struct drm_rect clip;
    277	int log_bpp;
    278
    279	ret = udl_log_cpp(fb->format->cpp[0]);
    280	if (ret < 0)
    281		return ret;
    282	log_bpp = ret;
    283
    284	ret = udl_aligned_damage_clip(&clip, x, y, width, height);
    285	if (ret)
    286		return ret;
    287	else if ((clip.x2 > fb->width) || (clip.y2 > fb->height))
    288		return -EINVAL;
    289
    290	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
    291	if (ret)
    292		return ret;
    293
    294	urb = udl_get_urb(dev);
    295	if (!urb) {
    296		ret = -ENOMEM;
    297		goto out_drm_gem_fb_end_cpu_access;
    298	}
    299	cmd = urb->transfer_buffer;
    300
    301	for (i = clip.y1; i < clip.y2; i++) {
    302		const int line_offset = fb->pitches[0] * i;
    303		const int byte_offset = line_offset + (clip.x1 << log_bpp);
    304		const int dev_byte_offset = (fb->width * i + clip.x1) << log_bpp;
    305		const int byte_width = (clip.x2 - clip.x1) << log_bpp;
    306		ret = udl_render_hline(dev, log_bpp, &urb, (char *)vaddr,
    307				       &cmd, byte_offset, dev_byte_offset,
    308				       byte_width);
    309		if (ret)
    310			goto out_drm_gem_fb_end_cpu_access;
    311	}
    312
    313	if (cmd > (char *)urb->transfer_buffer) {
    314		/* Send partial buffer remaining before exiting */
    315		int len;
    316		if (cmd < (char *)urb->transfer_buffer + urb->transfer_buffer_length)
    317			*cmd++ = 0xAF;
    318		len = cmd - (char *)urb->transfer_buffer;
    319		ret = udl_submit_urb(dev, urb, len);
    320	} else {
    321		udl_urb_completion(urb);
    322	}
    323
    324	ret = 0;
    325
    326out_drm_gem_fb_end_cpu_access:
    327	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
    328	return ret;
    329}
    330
    331/*
    332 * Simple display pipeline
    333 */
    334
    335static const uint32_t udl_simple_display_pipe_formats[] = {
    336	DRM_FORMAT_RGB565,
    337	DRM_FORMAT_XRGB8888,
    338};
    339
    340static enum drm_mode_status
    341udl_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
    342				   const struct drm_display_mode *mode)
    343{
    344	return MODE_OK;
    345}
    346
    347static void
    348udl_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe,
    349			       struct drm_crtc_state *crtc_state,
    350			       struct drm_plane_state *plane_state)
    351{
    352	struct drm_crtc *crtc = &pipe->crtc;
    353	struct drm_device *dev = crtc->dev;
    354	struct drm_framebuffer *fb = plane_state->fb;
    355	struct udl_device *udl = to_udl(dev);
    356	struct drm_display_mode *mode = &crtc_state->mode;
    357	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
    358	char *buf;
    359	char *wrptr;
    360	int color_depth = UDL_COLOR_DEPTH_16BPP;
    361
    362	buf = (char *)udl->mode_buf;
    363
    364	/* This first section has to do with setting the base address on the
    365	 * controller associated with the display. There are 2 base
    366	 * pointers, currently, we only use the 16 bpp segment.
    367	 */
    368	wrptr = udl_vidreg_lock(buf);
    369	wrptr = udl_set_color_depth(wrptr, color_depth);
    370	/* set base for 16bpp segment to 0 */
    371	wrptr = udl_set_base16bpp(wrptr, 0);
    372	/* set base for 8bpp segment to end of fb */
    373	wrptr = udl_set_base8bpp(wrptr, 2 * mode->vdisplay * mode->hdisplay);
    374
    375	wrptr = udl_set_vid_cmds(wrptr, mode);
    376	wrptr = udl_set_blank_mode(wrptr, UDL_BLANK_MODE_ON);
    377	wrptr = udl_vidreg_unlock(wrptr);
    378
    379	wrptr = udl_dummy_render(wrptr);
    380
    381	udl->mode_buf_len = wrptr - buf;
    382
    383	udl_handle_damage(fb, &shadow_plane_state->data[0], 0, 0, fb->width, fb->height);
    384
    385	if (!crtc_state->mode_changed)
    386		return;
    387
    388	/* enable display */
    389	udl_crtc_write_mode_to_hw(crtc);
    390}
    391
    392static void
    393udl_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe)
    394{
    395	struct drm_crtc *crtc = &pipe->crtc;
    396	struct drm_device *dev = crtc->dev;
    397	struct urb *urb;
    398	char *buf;
    399
    400	urb = udl_get_urb(dev);
    401	if (!urb)
    402		return;
    403
    404	buf = (char *)urb->transfer_buffer;
    405	buf = udl_vidreg_lock(buf);
    406	buf = udl_set_blank_mode(buf, UDL_BLANK_MODE_POWERDOWN);
    407	buf = udl_vidreg_unlock(buf);
    408	buf = udl_dummy_render(buf);
    409
    410	udl_submit_urb(dev, urb, buf - (char *)urb->transfer_buffer);
    411}
    412
    413static void
    414udl_simple_display_pipe_update(struct drm_simple_display_pipe *pipe,
    415			       struct drm_plane_state *old_plane_state)
    416{
    417	struct drm_plane_state *state = pipe->plane.state;
    418	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
    419	struct drm_framebuffer *fb = state->fb;
    420	struct drm_rect rect;
    421
    422	if (!fb)
    423		return;
    424
    425	if (drm_atomic_helper_damage_merged(old_plane_state, state, &rect))
    426		udl_handle_damage(fb, &shadow_plane_state->data[0], rect.x1, rect.y1,
    427				  rect.x2 - rect.x1, rect.y2 - rect.y1);
    428}
    429
    430static const struct drm_simple_display_pipe_funcs udl_simple_display_pipe_funcs = {
    431	.mode_valid = udl_simple_display_pipe_mode_valid,
    432	.enable = udl_simple_display_pipe_enable,
    433	.disable = udl_simple_display_pipe_disable,
    434	.update = udl_simple_display_pipe_update,
    435	DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
    436};
    437
    438/*
    439 * Modesetting
    440 */
    441
    442static const struct drm_mode_config_funcs udl_mode_funcs = {
    443	.fb_create = drm_gem_fb_create_with_dirty,
    444	.atomic_check  = drm_atomic_helper_check,
    445	.atomic_commit = drm_atomic_helper_commit,
    446};
    447
    448int udl_modeset_init(struct drm_device *dev)
    449{
    450	size_t format_count = ARRAY_SIZE(udl_simple_display_pipe_formats);
    451	struct udl_device *udl = to_udl(dev);
    452	struct drm_connector *connector;
    453	int ret;
    454
    455	ret = drmm_mode_config_init(dev);
    456	if (ret)
    457		return ret;
    458
    459	dev->mode_config.min_width = 640;
    460	dev->mode_config.min_height = 480;
    461
    462	dev->mode_config.max_width = 2048;
    463	dev->mode_config.max_height = 2048;
    464
    465	dev->mode_config.prefer_shadow = 0;
    466	dev->mode_config.preferred_depth = 16;
    467
    468	dev->mode_config.funcs = &udl_mode_funcs;
    469
    470	connector = udl_connector_init(dev);
    471	if (IS_ERR(connector))
    472		return PTR_ERR(connector);
    473
    474	format_count = ARRAY_SIZE(udl_simple_display_pipe_formats);
    475
    476	ret = drm_simple_display_pipe_init(dev, &udl->display_pipe,
    477					   &udl_simple_display_pipe_funcs,
    478					   udl_simple_display_pipe_formats,
    479					   format_count, NULL, connector);
    480	if (ret)
    481		return ret;
    482
    483	drm_mode_config_reset(dev);
    484
    485	return 0;
    486}