0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <drm/drm_atomic_helper.h>
0012 #include <drm/drm_crtc_helper.h>
0013 #include <drm/drm_damage_helper.h>
0014 #include <drm/drm_fourcc.h>
0015 #include <drm/drm_gem_atomic_helper.h>
0016 #include <drm/drm_gem_framebuffer_helper.h>
0017 #include <drm/drm_gem_shmem_helper.h>
0018 #include <drm/drm_modeset_helper_vtables.h>
0019 #include <drm/drm_vblank.h>
0020
0021 #include "udl_drv.h"
0022
0023 #define UDL_COLOR_DEPTH_16BPP 0
0024
0025
0026
0027
0028
0029 static char *udl_set_register(char *buf, u8 reg, u8 val)
0030 {
0031 *buf++ = 0xAF;
0032 *buf++ = 0x20;
0033 *buf++ = reg;
0034 *buf++ = val;
0035 return buf;
0036 }
0037
0038 static char *udl_vidreg_lock(char *buf)
0039 {
0040 return udl_set_register(buf, 0xFF, 0x00);
0041 }
0042
0043 static char *udl_vidreg_unlock(char *buf)
0044 {
0045 return udl_set_register(buf, 0xFF, 0xFF);
0046 }
0047
0048 static char *udl_set_blank_mode(char *buf, u8 mode)
0049 {
0050 return udl_set_register(buf, UDL_REG_BLANK_MODE, mode);
0051 }
0052
0053 static char *udl_set_color_depth(char *buf, u8 selection)
0054 {
0055 return udl_set_register(buf, 0x00, selection);
0056 }
0057
0058 static char *udl_set_base16bpp(char *wrptr, u32 base)
0059 {
0060
0061 wrptr = udl_set_register(wrptr, 0x20, base >> 16);
0062 wrptr = udl_set_register(wrptr, 0x21, base >> 8);
0063 return udl_set_register(wrptr, 0x22, base);
0064 }
0065
0066
0067
0068
0069
0070 static char *udl_set_base8bpp(char *wrptr, u32 base)
0071 {
0072 wrptr = udl_set_register(wrptr, 0x26, base >> 16);
0073 wrptr = udl_set_register(wrptr, 0x27, base >> 8);
0074 return udl_set_register(wrptr, 0x28, base);
0075 }
0076
0077 static char *udl_set_register_16(char *wrptr, u8 reg, u16 value)
0078 {
0079 wrptr = udl_set_register(wrptr, reg, value >> 8);
0080 return udl_set_register(wrptr, reg+1, value);
0081 }
0082
0083
0084
0085
0086
0087 static char *udl_set_register_16be(char *wrptr, u8 reg, u16 value)
0088 {
0089 wrptr = udl_set_register(wrptr, reg, value);
0090 return udl_set_register(wrptr, reg+1, value >> 8);
0091 }
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102 static u16 udl_lfsr16(u16 actual_count)
0103 {
0104 u32 lv = 0xFFFF;
0105
0106 while (actual_count--) {
0107 lv = ((lv << 1) |
0108 (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1))
0109 & 0xFFFF;
0110 }
0111
0112 return (u16) lv;
0113 }
0114
0115
0116
0117
0118
0119 static char *udl_set_register_lfsr16(char *wrptr, u8 reg, u16 value)
0120 {
0121 return udl_set_register_16(wrptr, reg, udl_lfsr16(value));
0122 }
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146 static char *udl_set_vid_cmds(char *wrptr, struct drm_display_mode *mode)
0147 {
0148 u16 xds, yds;
0149 u16 xde, yde;
0150 u16 yec;
0151
0152
0153 xds = mode->crtc_htotal - mode->crtc_hsync_start;
0154 wrptr = udl_set_register_lfsr16(wrptr, 0x01, xds);
0155
0156 xde = xds + mode->crtc_hdisplay;
0157 wrptr = udl_set_register_lfsr16(wrptr, 0x03, xde);
0158
0159
0160 yds = mode->crtc_vtotal - mode->crtc_vsync_start;
0161 wrptr = udl_set_register_lfsr16(wrptr, 0x05, yds);
0162
0163 yde = yds + mode->crtc_vdisplay;
0164 wrptr = udl_set_register_lfsr16(wrptr, 0x07, yde);
0165
0166
0167 wrptr = udl_set_register_lfsr16(wrptr, 0x09,
0168 mode->crtc_htotal - 1);
0169
0170
0171 wrptr = udl_set_register_lfsr16(wrptr, 0x0B, 1);
0172
0173
0174 wrptr = udl_set_register_lfsr16(wrptr, 0x0D,
0175 mode->crtc_hsync_end - mode->crtc_hsync_start + 1);
0176
0177
0178 wrptr = udl_set_register_16(wrptr, 0x0F, mode->hdisplay);
0179
0180
0181 yec = mode->crtc_vtotal;
0182 wrptr = udl_set_register_lfsr16(wrptr, 0x11, yec);
0183
0184
0185 wrptr = udl_set_register_lfsr16(wrptr, 0x13, 0);
0186
0187
0188 wrptr = udl_set_register_lfsr16(wrptr, 0x15, mode->crtc_vsync_end - mode->crtc_vsync_start);
0189
0190
0191 wrptr = udl_set_register_16(wrptr, 0x17, mode->crtc_vdisplay);
0192
0193 wrptr = udl_set_register_16be(wrptr, 0x1B,
0194 mode->clock / 5);
0195
0196 return wrptr;
0197 }
0198
0199 static char *udl_dummy_render(char *wrptr)
0200 {
0201 *wrptr++ = 0xAF;
0202 *wrptr++ = 0x6A;
0203 *wrptr++ = 0x00;
0204 *wrptr++ = 0x00;
0205 *wrptr++ = 0x00;
0206 *wrptr++ = 0x01;
0207 *wrptr++ = 0x00;
0208 *wrptr++ = 0x00;
0209 *wrptr++ = 0x00;
0210 return wrptr;
0211 }
0212
0213 static int udl_crtc_write_mode_to_hw(struct drm_crtc *crtc)
0214 {
0215 struct drm_device *dev = crtc->dev;
0216 struct udl_device *udl = to_udl(dev);
0217 struct urb *urb;
0218 char *buf;
0219 int retval;
0220
0221 if (udl->mode_buf_len == 0) {
0222 DRM_ERROR("No mode set\n");
0223 return -EINVAL;
0224 }
0225
0226 urb = udl_get_urb(dev);
0227 if (!urb)
0228 return -ENOMEM;
0229
0230 buf = (char *)urb->transfer_buffer;
0231
0232 memcpy(buf, udl->mode_buf, udl->mode_buf_len);
0233 retval = udl_submit_urb(dev, urb, udl->mode_buf_len);
0234 DRM_DEBUG("write mode info %d\n", udl->mode_buf_len);
0235 return retval;
0236 }
0237
0238 static long udl_log_cpp(unsigned int cpp)
0239 {
0240 if (WARN_ON(!is_power_of_2(cpp)))
0241 return -EINVAL;
0242 return __ffs(cpp);
0243 }
0244
0245 static int udl_aligned_damage_clip(struct drm_rect *clip, int x, int y,
0246 int width, int height)
0247 {
0248 int x1, x2;
0249
0250 if (WARN_ON_ONCE(x < 0) ||
0251 WARN_ON_ONCE(y < 0) ||
0252 WARN_ON_ONCE(width < 0) ||
0253 WARN_ON_ONCE(height < 0))
0254 return -EINVAL;
0255
0256 x1 = ALIGN_DOWN(x, sizeof(unsigned long));
0257 x2 = ALIGN(width + (x - x1), sizeof(unsigned long)) + x1;
0258
0259 clip->x1 = x1;
0260 clip->y1 = y;
0261 clip->x2 = x2;
0262 clip->y2 = y + height;
0263
0264 return 0;
0265 }
0266
0267 static int udl_handle_damage(struct drm_framebuffer *fb,
0268 const struct iosys_map *map,
0269 int x, int y, int width, int height)
0270 {
0271 struct drm_device *dev = fb->dev;
0272 void *vaddr = map->vaddr;
0273 int i, ret;
0274 char *cmd;
0275 struct urb *urb;
0276 struct drm_rect clip;
0277 int log_bpp;
0278
0279 ret = udl_log_cpp(fb->format->cpp[0]);
0280 if (ret < 0)
0281 return ret;
0282 log_bpp = ret;
0283
0284 ret = udl_aligned_damage_clip(&clip, x, y, width, height);
0285 if (ret)
0286 return ret;
0287 else if ((clip.x2 > fb->width) || (clip.y2 > fb->height))
0288 return -EINVAL;
0289
0290 ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
0291 if (ret)
0292 return ret;
0293
0294 urb = udl_get_urb(dev);
0295 if (!urb) {
0296 ret = -ENOMEM;
0297 goto out_drm_gem_fb_end_cpu_access;
0298 }
0299 cmd = urb->transfer_buffer;
0300
0301 for (i = clip.y1; i < clip.y2; i++) {
0302 const int line_offset = fb->pitches[0] * i;
0303 const int byte_offset = line_offset + (clip.x1 << log_bpp);
0304 const int dev_byte_offset = (fb->width * i + clip.x1) << log_bpp;
0305 const int byte_width = (clip.x2 - clip.x1) << log_bpp;
0306 ret = udl_render_hline(dev, log_bpp, &urb, (char *)vaddr,
0307 &cmd, byte_offset, dev_byte_offset,
0308 byte_width);
0309 if (ret)
0310 goto out_drm_gem_fb_end_cpu_access;
0311 }
0312
0313 if (cmd > (char *)urb->transfer_buffer) {
0314
0315 int len;
0316 if (cmd < (char *)urb->transfer_buffer + urb->transfer_buffer_length)
0317 *cmd++ = 0xAF;
0318 len = cmd - (char *)urb->transfer_buffer;
0319 ret = udl_submit_urb(dev, urb, len);
0320 } else {
0321 udl_urb_completion(urb);
0322 }
0323
0324 ret = 0;
0325
0326 out_drm_gem_fb_end_cpu_access:
0327 drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
0328 return ret;
0329 }
0330
0331
0332
0333
0334
0335 static const uint32_t udl_simple_display_pipe_formats[] = {
0336 DRM_FORMAT_RGB565,
0337 DRM_FORMAT_XRGB8888,
0338 };
0339
0340 static enum drm_mode_status
0341 udl_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
0342 const struct drm_display_mode *mode)
0343 {
0344 return MODE_OK;
0345 }
0346
0347 static void
0348 udl_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe,
0349 struct drm_crtc_state *crtc_state,
0350 struct drm_plane_state *plane_state)
0351 {
0352 struct drm_crtc *crtc = &pipe->crtc;
0353 struct drm_device *dev = crtc->dev;
0354 struct drm_framebuffer *fb = plane_state->fb;
0355 struct udl_device *udl = to_udl(dev);
0356 struct drm_display_mode *mode = &crtc_state->mode;
0357 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
0358 char *buf;
0359 char *wrptr;
0360 int color_depth = UDL_COLOR_DEPTH_16BPP;
0361
0362 buf = (char *)udl->mode_buf;
0363
0364
0365
0366
0367
0368 wrptr = udl_vidreg_lock(buf);
0369 wrptr = udl_set_color_depth(wrptr, color_depth);
0370
0371 wrptr = udl_set_base16bpp(wrptr, 0);
0372
0373 wrptr = udl_set_base8bpp(wrptr, 2 * mode->vdisplay * mode->hdisplay);
0374
0375 wrptr = udl_set_vid_cmds(wrptr, mode);
0376 wrptr = udl_set_blank_mode(wrptr, UDL_BLANK_MODE_ON);
0377 wrptr = udl_vidreg_unlock(wrptr);
0378
0379 wrptr = udl_dummy_render(wrptr);
0380
0381 udl->mode_buf_len = wrptr - buf;
0382
0383 udl_handle_damage(fb, &shadow_plane_state->data[0], 0, 0, fb->width, fb->height);
0384
0385 if (!crtc_state->mode_changed)
0386 return;
0387
0388
0389 udl_crtc_write_mode_to_hw(crtc);
0390 }
0391
0392 static void
0393 udl_simple_display_pipe_disable(struct drm_simple_display_pipe *pipe)
0394 {
0395 struct drm_crtc *crtc = &pipe->crtc;
0396 struct drm_device *dev = crtc->dev;
0397 struct urb *urb;
0398 char *buf;
0399
0400 urb = udl_get_urb(dev);
0401 if (!urb)
0402 return;
0403
0404 buf = (char *)urb->transfer_buffer;
0405 buf = udl_vidreg_lock(buf);
0406 buf = udl_set_blank_mode(buf, UDL_BLANK_MODE_POWERDOWN);
0407 buf = udl_vidreg_unlock(buf);
0408 buf = udl_dummy_render(buf);
0409
0410 udl_submit_urb(dev, urb, buf - (char *)urb->transfer_buffer);
0411 }
0412
0413 static void
0414 udl_simple_display_pipe_update(struct drm_simple_display_pipe *pipe,
0415 struct drm_plane_state *old_plane_state)
0416 {
0417 struct drm_plane_state *state = pipe->plane.state;
0418 struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
0419 struct drm_framebuffer *fb = state->fb;
0420 struct drm_rect rect;
0421
0422 if (!fb)
0423 return;
0424
0425 if (drm_atomic_helper_damage_merged(old_plane_state, state, &rect))
0426 udl_handle_damage(fb, &shadow_plane_state->data[0], rect.x1, rect.y1,
0427 rect.x2 - rect.x1, rect.y2 - rect.y1);
0428 }
0429
0430 static const struct drm_simple_display_pipe_funcs udl_simple_display_pipe_funcs = {
0431 .mode_valid = udl_simple_display_pipe_mode_valid,
0432 .enable = udl_simple_display_pipe_enable,
0433 .disable = udl_simple_display_pipe_disable,
0434 .update = udl_simple_display_pipe_update,
0435 DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
0436 };
0437
0438
0439
0440
0441
0442 static const struct drm_mode_config_funcs udl_mode_funcs = {
0443 .fb_create = drm_gem_fb_create_with_dirty,
0444 .atomic_check = drm_atomic_helper_check,
0445 .atomic_commit = drm_atomic_helper_commit,
0446 };
0447
0448 int udl_modeset_init(struct drm_device *dev)
0449 {
0450 size_t format_count = ARRAY_SIZE(udl_simple_display_pipe_formats);
0451 struct udl_device *udl = to_udl(dev);
0452 struct drm_connector *connector;
0453 int ret;
0454
0455 ret = drmm_mode_config_init(dev);
0456 if (ret)
0457 return ret;
0458
0459 dev->mode_config.min_width = 640;
0460 dev->mode_config.min_height = 480;
0461
0462 dev->mode_config.max_width = 2048;
0463 dev->mode_config.max_height = 2048;
0464
0465 dev->mode_config.prefer_shadow = 0;
0466 dev->mode_config.preferred_depth = 16;
0467
0468 dev->mode_config.funcs = &udl_mode_funcs;
0469
0470 connector = udl_connector_init(dev);
0471 if (IS_ERR(connector))
0472 return PTR_ERR(connector);
0473
0474 format_count = ARRAY_SIZE(udl_simple_display_pipe_formats);
0475
0476 ret = drm_simple_display_pipe_init(dev, &udl->display_pipe,
0477 &udl_simple_display_pipe_funcs,
0478 udl_simple_display_pipe_formats,
0479 format_count, NULL, connector);
0480 if (ret)
0481 return ret;
0482
0483 drm_mode_config_reset(dev);
0484
0485 return 0;
0486 }