Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.0-rc1 455 lines 12 kB view raw
1/* 2 * Copyright (C) 2016 Noralf Trønnes 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 */ 9 10#include <linux/backlight.h> 11#include <linux/dma-buf.h> 12#include <linux/module.h> 13#include <linux/pm.h> 14#include <linux/spi/spi.h> 15#include <linux/swab.h> 16 17#include <drm/drm_device.h> 18#include <drm/drm_drv.h> 19#include <drm/drm_fourcc.h> 20#include <drm/drm_print.h> 21#include <drm/tinydrm/tinydrm.h> 22#include <drm/tinydrm/tinydrm-helpers.h> 23#include <uapi/drm/drm.h> 24 25static unsigned int spi_max; 26module_param(spi_max, uint, 0400); 27MODULE_PARM_DESC(spi_max, "Set a lower SPI max transfer size"); 28 29/** 30 * tinydrm_merge_clips - Merge clip rectangles 31 * @dst: Destination clip rectangle 32 * @src: Source clip rectangle(s) 33 * @num_clips: Number of @src clip rectangles 34 * @flags: Dirty fb ioctl flags 35 * @max_width: Maximum width of @dst 36 * @max_height: Maximum height of @dst 37 * 38 * This function merges @src clip rectangle(s) into @dst. If @src is NULL, 39 * @max_width and @min_width is used to set a full @dst clip rectangle. 40 * 41 * Returns: 42 * true if it's a full clip, false otherwise 43 */ 44bool tinydrm_merge_clips(struct drm_clip_rect *dst, 45 struct drm_clip_rect *src, unsigned int num_clips, 46 unsigned int flags, u32 max_width, u32 max_height) 47{ 48 unsigned int i; 49 50 if (!src || !num_clips) { 51 dst->x1 = 0; 52 dst->x2 = max_width; 53 dst->y1 = 0; 54 dst->y2 = max_height; 55 return true; 56 } 57 58 dst->x1 = ~0; 59 dst->y1 = ~0; 60 dst->x2 = 0; 61 dst->y2 = 0; 62 63 for (i = 0; i < num_clips; i++) { 64 if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) 65 i++; 66 dst->x1 = min(dst->x1, src[i].x1); 67 dst->x2 = max(dst->x2, src[i].x2); 68 dst->y1 = min(dst->y1, src[i].y1); 69 dst->y2 = max(dst->y2, src[i].y2); 70 } 71 72 if (dst->x2 > max_width || dst->y2 > max_height || 73 dst->x1 >= dst->x2 || dst->y1 >= dst->y2) { 74 DRM_DEBUG_KMS("Illegal clip: x1=%u, x2=%u, y1=%u, y2=%u\n", 75 dst->x1, dst->x2, dst->y1, dst->y2); 76 dst->x1 = 0; 77 dst->y1 = 0; 78 dst->x2 = max_width; 79 dst->y2 = max_height; 80 } 81 82 return (dst->x2 - dst->x1) == max_width && 83 (dst->y2 - dst->y1) == max_height; 84} 85EXPORT_SYMBOL(tinydrm_merge_clips); 86 87int tinydrm_fb_dirty(struct drm_framebuffer *fb, 88 struct drm_file *file_priv, 89 unsigned int flags, unsigned int color, 90 struct drm_clip_rect *clips, 91 unsigned int num_clips) 92{ 93 struct tinydrm_device *tdev = fb->dev->dev_private; 94 struct drm_plane *plane = &tdev->pipe.plane; 95 int ret = 0; 96 97 drm_modeset_lock(&plane->mutex, NULL); 98 99 /* fbdev can flush even when we're not interested */ 100 if (plane->state->fb == fb) { 101 mutex_lock(&tdev->dirty_lock); 102 ret = tdev->fb_dirty(fb, file_priv, flags, 103 color, clips, num_clips); 104 mutex_unlock(&tdev->dirty_lock); 105 } 106 107 drm_modeset_unlock(&plane->mutex); 108 109 if (ret) 110 dev_err_once(fb->dev->dev, 111 "Failed to update display %d\n", ret); 112 113 return ret; 114} 115EXPORT_SYMBOL(tinydrm_fb_dirty); 116 117/** 118 * tinydrm_memcpy - Copy clip buffer 119 * @dst: Destination buffer 120 * @vaddr: Source buffer 121 * @fb: DRM framebuffer 122 * @clip: Clip rectangle area to copy 123 */ 124void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, 125 struct drm_clip_rect *clip) 126{ 127 unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0); 128 unsigned int pitch = fb->pitches[0]; 129 void *src = vaddr + (clip->y1 * pitch) + (clip->x1 * cpp); 130 size_t len = (clip->x2 - clip->x1) * cpp; 131 unsigned int y; 132 133 for (y = clip->y1; y < clip->y2; y++) { 134 memcpy(dst, src, len); 135 src += pitch; 136 dst += len; 137 } 138} 139EXPORT_SYMBOL(tinydrm_memcpy); 140 141/** 142 * tinydrm_swab16 - Swap bytes into clip buffer 143 * @dst: RGB565 destination buffer 144 * @vaddr: RGB565 source buffer 145 * @fb: DRM framebuffer 146 * @clip: Clip rectangle area to copy 147 */ 148void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb, 149 struct drm_clip_rect *clip) 150{ 151 size_t len = (clip->x2 - clip->x1) * sizeof(u16); 152 unsigned int x, y; 153 u16 *src, *buf; 154 155 /* 156 * The cma memory is write-combined so reads are uncached. 157 * Speed up by fetching one line at a time. 158 */ 159 buf = kmalloc(len, GFP_KERNEL); 160 if (!buf) 161 return; 162 163 for (y = clip->y1; y < clip->y2; y++) { 164 src = vaddr + (y * fb->pitches[0]); 165 src += clip->x1; 166 memcpy(buf, src, len); 167 src = buf; 168 for (x = clip->x1; x < clip->x2; x++) 169 *dst++ = swab16(*src++); 170 } 171 172 kfree(buf); 173} 174EXPORT_SYMBOL(tinydrm_swab16); 175 176/** 177 * tinydrm_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer 178 * @dst: RGB565 destination buffer 179 * @vaddr: XRGB8888 source buffer 180 * @fb: DRM framebuffer 181 * @clip: Clip rectangle area to copy 182 * @swap: Swap bytes 183 * 184 * Drivers can use this function for RGB565 devices that don't natively 185 * support XRGB8888. 186 */ 187void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr, 188 struct drm_framebuffer *fb, 189 struct drm_clip_rect *clip, bool swap) 190{ 191 size_t len = (clip->x2 - clip->x1) * sizeof(u32); 192 unsigned int x, y; 193 u32 *src, *buf; 194 u16 val16; 195 196 buf = kmalloc(len, GFP_KERNEL); 197 if (!buf) 198 return; 199 200 for (y = clip->y1; y < clip->y2; y++) { 201 src = vaddr + (y * fb->pitches[0]); 202 src += clip->x1; 203 memcpy(buf, src, len); 204 src = buf; 205 for (x = clip->x1; x < clip->x2; x++) { 206 val16 = ((*src & 0x00F80000) >> 8) | 207 ((*src & 0x0000FC00) >> 5) | 208 ((*src & 0x000000F8) >> 3); 209 src++; 210 if (swap) 211 *dst++ = swab16(val16); 212 else 213 *dst++ = val16; 214 } 215 } 216 217 kfree(buf); 218} 219EXPORT_SYMBOL(tinydrm_xrgb8888_to_rgb565); 220 221/** 222 * tinydrm_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale 223 * @dst: 8-bit grayscale destination buffer 224 * @vaddr: XRGB8888 source buffer 225 * @fb: DRM framebuffer 226 * @clip: Clip rectangle area to copy 227 * 228 * Drm doesn't have native monochrome or grayscale support. 229 * Such drivers can announce the commonly supported XR24 format to userspace 230 * and use this function to convert to the native format. 231 * 232 * Monochrome drivers will use the most significant bit, 233 * where 1 means foreground color and 0 background color. 234 * 235 * ITU BT.601 is used for the RGB -> luma (brightness) conversion. 236 */ 237void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb, 238 struct drm_clip_rect *clip) 239{ 240 unsigned int len = (clip->x2 - clip->x1) * sizeof(u32); 241 unsigned int x, y; 242 void *buf; 243 u32 *src; 244 245 if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888)) 246 return; 247 /* 248 * The cma memory is write-combined so reads are uncached. 249 * Speed up by fetching one line at a time. 250 */ 251 buf = kmalloc(len, GFP_KERNEL); 252 if (!buf) 253 return; 254 255 for (y = clip->y1; y < clip->y2; y++) { 256 src = vaddr + (y * fb->pitches[0]); 257 src += clip->x1; 258 memcpy(buf, src, len); 259 src = buf; 260 for (x = clip->x1; x < clip->x2; x++) { 261 u8 r = (*src & 0x00ff0000) >> 16; 262 u8 g = (*src & 0x0000ff00) >> 8; 263 u8 b = *src & 0x000000ff; 264 265 /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ 266 *dst++ = (3 * r + 6 * g + b) / 10; 267 src++; 268 } 269 } 270 271 kfree(buf); 272} 273EXPORT_SYMBOL(tinydrm_xrgb8888_to_gray8); 274 275#if IS_ENABLED(CONFIG_SPI) 276 277/** 278 * tinydrm_spi_max_transfer_size - Determine max SPI transfer size 279 * @spi: SPI device 280 * @max_len: Maximum buffer size needed (optional) 281 * 282 * This function returns the maximum size to use for SPI transfers. It checks 283 * the SPI master, the optional @max_len and the module parameter spi_max and 284 * returns the smallest. 285 * 286 * Returns: 287 * Maximum size for SPI transfers 288 */ 289size_t tinydrm_spi_max_transfer_size(struct spi_device *spi, size_t max_len) 290{ 291 size_t ret; 292 293 ret = min(spi_max_transfer_size(spi), spi->master->max_dma_len); 294 if (max_len) 295 ret = min(ret, max_len); 296 if (spi_max) 297 ret = min_t(size_t, ret, spi_max); 298 ret &= ~0x3; 299 if (ret < 4) 300 ret = 4; 301 302 return ret; 303} 304EXPORT_SYMBOL(tinydrm_spi_max_transfer_size); 305 306/** 307 * tinydrm_spi_bpw_supported - Check if bits per word is supported 308 * @spi: SPI device 309 * @bpw: Bits per word 310 * 311 * This function checks to see if the SPI master driver supports @bpw. 312 * 313 * Returns: 314 * True if @bpw is supported, false otherwise. 315 */ 316bool tinydrm_spi_bpw_supported(struct spi_device *spi, u8 bpw) 317{ 318 u32 bpw_mask = spi->master->bits_per_word_mask; 319 320 if (bpw == 8) 321 return true; 322 323 if (!bpw_mask) { 324 dev_warn_once(&spi->dev, 325 "bits_per_word_mask not set, assume 8-bit only\n"); 326 return false; 327 } 328 329 if (bpw_mask & SPI_BPW_MASK(bpw)) 330 return true; 331 332 return false; 333} 334EXPORT_SYMBOL(tinydrm_spi_bpw_supported); 335 336static void 337tinydrm_dbg_spi_print(struct spi_device *spi, struct spi_transfer *tr, 338 const void *buf, int idx, bool tx) 339{ 340 u32 speed_hz = tr->speed_hz ? tr->speed_hz : spi->max_speed_hz; 341 char linebuf[3 * 32]; 342 343 hex_dump_to_buffer(buf, tr->len, 16, 344 DIV_ROUND_UP(tr->bits_per_word, 8), 345 linebuf, sizeof(linebuf), false); 346 347 printk(KERN_DEBUG 348 " tr(%i): speed=%u%s, bpw=%i, len=%u, %s_buf=[%s%s]\n", idx, 349 speed_hz > 1000000 ? speed_hz / 1000000 : speed_hz / 1000, 350 speed_hz > 1000000 ? "MHz" : "kHz", tr->bits_per_word, tr->len, 351 tx ? "tx" : "rx", linebuf, tr->len > 16 ? " ..." : ""); 352} 353 354/* called through tinydrm_dbg_spi_message() */ 355void _tinydrm_dbg_spi_message(struct spi_device *spi, struct spi_message *m) 356{ 357 struct spi_transfer *tmp; 358 int i = 0; 359 360 list_for_each_entry(tmp, &m->transfers, transfer_list) { 361 362 if (tmp->tx_buf) 363 tinydrm_dbg_spi_print(spi, tmp, tmp->tx_buf, i, true); 364 if (tmp->rx_buf) 365 tinydrm_dbg_spi_print(spi, tmp, tmp->rx_buf, i, false); 366 i++; 367 } 368} 369EXPORT_SYMBOL(_tinydrm_dbg_spi_message); 370 371/** 372 * tinydrm_spi_transfer - SPI transfer helper 373 * @spi: SPI device 374 * @speed_hz: Override speed (optional) 375 * @header: Optional header transfer 376 * @bpw: Bits per word 377 * @buf: Buffer to transfer 378 * @len: Buffer length 379 * 380 * This SPI transfer helper breaks up the transfer of @buf into chunks which 381 * the SPI master driver can handle. If the machine is Little Endian and the 382 * SPI master driver doesn't support 16 bits per word, it swaps the bytes and 383 * does a 8-bit transfer. 384 * If @header is set, it is prepended to each SPI message. 385 * 386 * Returns: 387 * Zero on success, negative error code on failure. 388 */ 389int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz, 390 struct spi_transfer *header, u8 bpw, const void *buf, 391 size_t len) 392{ 393 struct spi_transfer tr = { 394 .bits_per_word = bpw, 395 .speed_hz = speed_hz, 396 }; 397 struct spi_message m; 398 u16 *swap_buf = NULL; 399 size_t max_chunk; 400 size_t chunk; 401 int ret = 0; 402 403 if (WARN_ON_ONCE(bpw != 8 && bpw != 16)) 404 return -EINVAL; 405 406 max_chunk = tinydrm_spi_max_transfer_size(spi, 0); 407 408 if (drm_debug & DRM_UT_DRIVER) 409 pr_debug("[drm:%s] bpw=%u, max_chunk=%zu, transfers:\n", 410 __func__, bpw, max_chunk); 411 412 if (bpw == 16 && !tinydrm_spi_bpw_supported(spi, 16)) { 413 tr.bits_per_word = 8; 414 if (tinydrm_machine_little_endian()) { 415 swap_buf = kmalloc(min(len, max_chunk), GFP_KERNEL); 416 if (!swap_buf) 417 return -ENOMEM; 418 } 419 } 420 421 spi_message_init(&m); 422 if (header) 423 spi_message_add_tail(header, &m); 424 spi_message_add_tail(&tr, &m); 425 426 while (len) { 427 chunk = min(len, max_chunk); 428 429 tr.tx_buf = buf; 430 tr.len = chunk; 431 432 if (swap_buf) { 433 const u16 *buf16 = buf; 434 unsigned int i; 435 436 for (i = 0; i < chunk / 2; i++) 437 swap_buf[i] = swab16(buf16[i]); 438 439 tr.tx_buf = swap_buf; 440 } 441 442 buf += chunk; 443 len -= chunk; 444 445 tinydrm_dbg_spi_message(spi, &m); 446 ret = spi_sync(spi, &m); 447 if (ret) 448 return ret; 449 } 450 451 return 0; 452} 453EXPORT_SYMBOL(tinydrm_spi_transfer); 454 455#endif /* CONFIG_SPI */