linux-stable/drivers/gpu/drm/tiny/repaper.c

1150 lines
28 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DRM driver for Pervasive Displays RePaper branded e-ink panels
*
* Copyright 2013-2017 Pervasive Displays, Inc.
* Copyright 2017 Noralf Trønnes
*
* The driver supports:
* Material Film: Aurora Mb (V231)
* Driver IC: G2 (eTC)
*
* The controller code was taken from the userspace driver:
* https://github.com/repaper/gratis
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/sched/clock.h>
#include <linux/spi/spi.h>
#include <linux/thermal.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
drm/fb: rename FB CMA helpers to FB DMA helpers Rename "FB CMA" helpers to "FB DMA" helpers - considering the hierarchy of APIs (mm/cma -> dma -> fb dma) calling them "FB DMA" seems to be more applicable. Besides that, commit e57924d4ae80 ("drm/doc: Task to rename CMA helpers") requests to rename the CMA helpers and implies that people seem to be confused about the naming. In order to do this renaming the following script was used: ``` #!/bin/bash DIRS="drivers/gpu include/drm Documentation/gpu" REGEX_SYM_UPPER="[0-9A-Z_\-]" REGEX_SYM_LOWER="[0-9a-z_\-]" REGEX_GREP_UPPER="(${REGEX_SYM_UPPER}*)(FB)_CMA_(${REGEX_SYM_UPPER}*)" REGEX_GREP_LOWER="(${REGEX_SYM_LOWER}*)(fb)_cma_(${REGEX_SYM_LOWER}*)" REGEX_SED_UPPER="s/${REGEX_GREP_UPPER}/\1\2_DMA_\3/g" REGEX_SED_LOWER="s/${REGEX_GREP_LOWER}/\1\2_dma_\3/g" # Find all upper case 'CMA' symbols and replace them with 'DMA'. for ff in $(grep -REHl "${REGEX_GREP_UPPER}" $DIRS) do sed -i -E "$REGEX_SED_UPPER" $ff done # Find all lower case 'cma' symbols and replace them with 'dma'. for ff in $(grep -REHl "${REGEX_GREP_LOWER}" $DIRS) do sed -i -E "$REGEX_SED_LOWER" $ff done # Replace all occurrences of 'CMA' / 'cma' in comments and # documentation files with 'DMA' / 'dma'. for ff in $(grep -RiHl " cma " $DIRS) do sed -i -E "s/ cma / dma /g" $ff sed -i -E "s/ CMA / DMA /g" $ff done ``` Only a few more manual modifications were needed, e.g. reverting the following modifications in some DRM Kconfig files - select CMA if HAVE_DMA_CONTIGUOUS + select DMA if HAVE_DMA_CONTIGUOUS as well as manually picking the occurrences of 'CMA'/'cma' in comments and documentation which relate to "FB CMA", but not "GEM CMA". This patch is compile-time tested building a x86_64 kernel with `make allyesconfig && make drivers/gpu/drm`. Acked-by: Sam Ravnborg <sam@ravnborg.org> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Danilo Krummrich <dakr@redhat.com> Reviewed-by: Liviu Dudau <liviu.dudau@arm.com> #drivers/gpu/drm/arm Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Link: https://patchwork.freedesktop.org/patch/msgid/20220802000405.949236-3-dakr@redhat.com
2022-08-02 00:04:02 +00:00
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
drm/gem: rename GEM CMA helpers to GEM DMA helpers Rename "GEM CMA" helpers to "GEM DMA" helpers - considering the hierarchy of APIs (mm/cma -> dma -> gem dma) calling them "GEM DMA" seems to be more applicable. Besides that, commit e57924d4ae80 ("drm/doc: Task to rename CMA helpers") requests to rename the CMA helpers and implies that people seem to be confused about the naming. In order to do this renaming the following script was used: ``` #!/bin/bash DIRS="drivers/gpu include/drm Documentation/gpu" REGEX_SYM_UPPER="[0-9A-Z_\-]" REGEX_SYM_LOWER="[0-9a-z_\-]" REGEX_GREP_UPPER="(${REGEX_SYM_UPPER}*)(GEM)_CMA_(${REGEX_SYM_UPPER}*)" REGEX_GREP_LOWER="(${REGEX_SYM_LOWER}*)(gem)_cma_(${REGEX_SYM_LOWER}*)" REGEX_SED_UPPER="s/${REGEX_GREP_UPPER}/\1\2_DMA_\3/g" REGEX_SED_LOWER="s/${REGEX_GREP_LOWER}/\1\2_dma_\3/g" # Find all upper case 'CMA' symbols and replace them with 'DMA'. for ff in $(grep -REHl "${REGEX_GREP_UPPER}" $DIRS) do sed -i -E "$REGEX_SED_UPPER" $ff done # Find all lower case 'cma' symbols and replace them with 'dma'. for ff in $(grep -REHl "${REGEX_GREP_LOWER}" $DIRS) do sed -i -E "$REGEX_SED_LOWER" $ff done # Replace all occurrences of 'CMA' / 'cma' in comments and # documentation files with 'DMA' / 'dma'. for ff in $(grep -RiHl " cma " $DIRS) do sed -i -E "s/ cma / dma /g" $ff sed -i -E "s/ CMA / DMA /g" $ff done # Rename all 'cma_obj's to 'dma_obj'. for ff in $(grep -RiHl "cma_obj" $DIRS) do sed -i -E "s/cma_obj/dma_obj/g" $ff done ``` Only a few more manual modifications were needed, e.g. reverting the following modifications in some DRM Kconfig files - select CMA if HAVE_DMA_CONTIGUOUS + select DMA if HAVE_DMA_CONTIGUOUS as well as manually picking the occurrences of 'CMA'/'cma' in comments and documentation which relate to "GEM CMA", but not "FB CMA". Also drivers/gpu/drm/Makefile was fixed up manually after renaming drm_gem_cma_helper.c to drm_gem_dma_helper.c. This patch is compile-time tested building a x86_64 kernel with `make allyesconfig && make drivers/gpu/drm`. Acked-by: Sam Ravnborg <sam@ravnborg.org> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Danilo Krummrich <dakr@redhat.com> Reviewed-by: Liviu Dudau <liviu.dudau@arm.com> #drivers/gpu/drm/arm Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Link: https://patchwork.freedesktop.org/patch/msgid/20220802000405.949236-4-dakr@redhat.com
2022-08-02 00:04:03 +00:00
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_modes.h>
#include <drm/drm_rect.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#define REPAPER_RID_G2_COG_ID 0x12
enum repaper_model {
/* 0 is reserved to avoid clashing with NULL */
E1144CS021 = 1,
E1190CS021,
E2200CS021,
E2271CS021,
};
enum repaper_stage { /* Image pixel -> Display pixel */
REPAPER_COMPENSATE, /* B -> W, W -> B (Current Image) */
REPAPER_WHITE, /* B -> N, W -> W (Current Image) */
REPAPER_INVERSE, /* B -> N, W -> B (New Image) */
REPAPER_NORMAL /* B -> B, W -> W (New Image) */
};
enum repaper_epd_border_byte {
REPAPER_BORDER_BYTE_NONE,
REPAPER_BORDER_BYTE_ZERO,
REPAPER_BORDER_BYTE_SET,
};
struct repaper_epd {
struct drm_device drm;
struct drm_simple_display_pipe pipe;
const struct drm_display_mode *mode;
struct drm_connector connector;
struct spi_device *spi;
struct gpio_desc *panel_on;
struct gpio_desc *border;
struct gpio_desc *discharge;
struct gpio_desc *reset;
struct gpio_desc *busy;
struct thermal_zone_device *thermal;
unsigned int height;
unsigned int width;
unsigned int bytes_per_scan;
const u8 *channel_select;
unsigned int stage_time;
unsigned int factored_stage_time;
bool middle_scan;
bool pre_border_byte;
enum repaper_epd_border_byte border_byte;
u8 *line_buffer;
void *current_frame;
bool cleared;
bool partial;
};
static inline struct repaper_epd *drm_to_epd(struct drm_device *drm)
{
return container_of(drm, struct repaper_epd, drm);
}
static int repaper_spi_transfer(struct spi_device *spi, u8 header,
const void *tx, void *rx, size_t len)
{
void *txbuf = NULL, *rxbuf = NULL;
struct spi_transfer tr[2] = {};
u8 *headerbuf;
int ret;
headerbuf = kmalloc(1, GFP_KERNEL);
if (!headerbuf)
return -ENOMEM;
headerbuf[0] = header;
tr[0].tx_buf = headerbuf;
tr[0].len = 1;
/* Stack allocated tx? */
if (tx && len <= 32) {
txbuf = kmemdup(tx, len, GFP_KERNEL);
if (!txbuf) {
ret = -ENOMEM;
goto out_free;
}
}
if (rx) {
rxbuf = kmalloc(len, GFP_KERNEL);
if (!rxbuf) {
ret = -ENOMEM;
goto out_free;
}
}
tr[1].tx_buf = txbuf ? txbuf : tx;
tr[1].rx_buf = rxbuf;
tr[1].len = len;
ndelay(80);
ret = spi_sync_transfer(spi, tr, 2);
if (rx && !ret)
memcpy(rx, rxbuf, len);
out_free:
kfree(headerbuf);
kfree(txbuf);
kfree(rxbuf);
return ret;
}
static int repaper_write_buf(struct spi_device *spi, u8 reg,
const u8 *buf, size_t len)
{
int ret;
ret = repaper_spi_transfer(spi, 0x70, &reg, NULL, 1);
if (ret)
return ret;
return repaper_spi_transfer(spi, 0x72, buf, NULL, len);
}
static int repaper_write_val(struct spi_device *spi, u8 reg, u8 val)
{
return repaper_write_buf(spi, reg, &val, 1);
}
static int repaper_read_val(struct spi_device *spi, u8 reg)
{
int ret;
u8 val;
ret = repaper_spi_transfer(spi, 0x70, &reg, NULL, 1);
if (ret)
return ret;
ret = repaper_spi_transfer(spi, 0x73, NULL, &val, 1);
return ret ? ret : val;
}
static int repaper_read_id(struct spi_device *spi)
{
int ret;
u8 id;
ret = repaper_spi_transfer(spi, 0x71, NULL, &id, 1);
return ret ? ret : id;
}
static void repaper_spi_mosi_low(struct spi_device *spi)
{
const u8 buf[1] = { 0 };
spi_write(spi, buf, 1);
}
/* pixels on display are numbered from 1 so even is actually bits 1,3,5,... */
static void repaper_even_pixels(struct repaper_epd *epd, u8 **pp,
const u8 *data, u8 fixed_value, const u8 *mask,
enum repaper_stage stage)
{
unsigned int b;
for (b = 0; b < (epd->width / 8); b++) {
if (data) {
u8 pixels = data[b] & 0xaa;
u8 pixel_mask = 0xff;
u8 p1, p2, p3, p4;
if (mask) {
pixel_mask = (mask[b] ^ pixels) & 0xaa;
pixel_mask |= pixel_mask >> 1;
}
switch (stage) {
case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */
pixels = 0xaa | ((pixels ^ 0xaa) >> 1);
break;
case REPAPER_WHITE: /* B -> N, W -> W (Current) */
pixels = 0x55 + ((pixels ^ 0xaa) >> 1);
break;
case REPAPER_INVERSE: /* B -> N, W -> B (New) */
pixels = 0x55 | (pixels ^ 0xaa);
break;
case REPAPER_NORMAL: /* B -> B, W -> W (New) */
pixels = 0xaa | (pixels >> 1);
break;
}
pixels = (pixels & pixel_mask) | (~pixel_mask & 0x55);
p1 = (pixels >> 6) & 0x03;
p2 = (pixels >> 4) & 0x03;
p3 = (pixels >> 2) & 0x03;
p4 = (pixels >> 0) & 0x03;
pixels = (p1 << 0) | (p2 << 2) | (p3 << 4) | (p4 << 6);
*(*pp)++ = pixels;
} else {
*(*pp)++ = fixed_value;
}
}
}
/* pixels on display are numbered from 1 so odd is actually bits 0,2,4,... */
static void repaper_odd_pixels(struct repaper_epd *epd, u8 **pp,
const u8 *data, u8 fixed_value, const u8 *mask,
enum repaper_stage stage)
{
unsigned int b;
for (b = epd->width / 8; b > 0; b--) {
if (data) {
u8 pixels = data[b - 1] & 0x55;
u8 pixel_mask = 0xff;
if (mask) {
pixel_mask = (mask[b - 1] ^ pixels) & 0x55;
pixel_mask |= pixel_mask << 1;
}
switch (stage) {
case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */
pixels = 0xaa | (pixels ^ 0x55);
break;
case REPAPER_WHITE: /* B -> N, W -> W (Current) */
pixels = 0x55 + (pixels ^ 0x55);
break;
case REPAPER_INVERSE: /* B -> N, W -> B (New) */
pixels = 0x55 | ((pixels ^ 0x55) << 1);
break;
case REPAPER_NORMAL: /* B -> B, W -> W (New) */
pixels = 0xaa | pixels;
break;
}
pixels = (pixels & pixel_mask) | (~pixel_mask & 0x55);
*(*pp)++ = pixels;
} else {
*(*pp)++ = fixed_value;
}
}
}
/* interleave bits: (byte)76543210 -> (16 bit).7.6.5.4.3.2.1 */
static inline u16 repaper_interleave_bits(u16 value)
{
value = (value | (value << 4)) & 0x0f0f;
value = (value | (value << 2)) & 0x3333;
value = (value | (value << 1)) & 0x5555;
return value;
}
/* pixels on display are numbered from 1 */
static void repaper_all_pixels(struct repaper_epd *epd, u8 **pp,
const u8 *data, u8 fixed_value, const u8 *mask,
enum repaper_stage stage)
{
unsigned int b;
for (b = epd->width / 8; b > 0; b--) {
if (data) {
u16 pixels = repaper_interleave_bits(data[b - 1]);
u16 pixel_mask = 0xffff;
if (mask) {
pixel_mask = repaper_interleave_bits(mask[b - 1]);
pixel_mask = (pixel_mask ^ pixels) & 0x5555;
pixel_mask |= pixel_mask << 1;
}
switch (stage) {
case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */
pixels = 0xaaaa | (pixels ^ 0x5555);
break;
case REPAPER_WHITE: /* B -> N, W -> W (Current) */
pixels = 0x5555 + (pixels ^ 0x5555);
break;
case REPAPER_INVERSE: /* B -> N, W -> B (New) */
pixels = 0x5555 | ((pixels ^ 0x5555) << 1);
break;
case REPAPER_NORMAL: /* B -> B, W -> W (New) */
pixels = 0xaaaa | pixels;
break;
}
pixels = (pixels & pixel_mask) | (~pixel_mask & 0x5555);
*(*pp)++ = pixels >> 8;
*(*pp)++ = pixels;
} else {
*(*pp)++ = fixed_value;
*(*pp)++ = fixed_value;
}
}
}
/* output one line of scan and data bytes to the display */
static void repaper_one_line(struct repaper_epd *epd, unsigned int line,
const u8 *data, u8 fixed_value, const u8 *mask,
enum repaper_stage stage)
{
u8 *p = epd->line_buffer;
unsigned int b;
repaper_spi_mosi_low(epd->spi);
if (epd->pre_border_byte)
*p++ = 0x00;
if (epd->middle_scan) {
/* data bytes */
repaper_odd_pixels(epd, &p, data, fixed_value, mask, stage);
/* scan line */
for (b = epd->bytes_per_scan; b > 0; b--) {
if (line / 4 == b - 1)
*p++ = 0x03 << (2 * (line & 0x03));
else
*p++ = 0x00;
}
/* data bytes */
repaper_even_pixels(epd, &p, data, fixed_value, mask, stage);
} else {
/*
* even scan line, but as lines on display are numbered from 1,
* line: 1,3,5,...
*/
for (b = 0; b < epd->bytes_per_scan; b++) {
if (0 != (line & 0x01) && line / 8 == b)
*p++ = 0xc0 >> (line & 0x06);
else
*p++ = 0x00;
}
/* data bytes */
repaper_all_pixels(epd, &p, data, fixed_value, mask, stage);
/*
* odd scan line, but as lines on display are numbered from 1,
* line: 0,2,4,6,...
*/
for (b = epd->bytes_per_scan; b > 0; b--) {
if (0 == (line & 0x01) && line / 8 == b - 1)
*p++ = 0x03 << (line & 0x06);
else
*p++ = 0x00;
}
}
switch (epd->border_byte) {
case REPAPER_BORDER_BYTE_NONE:
break;
case REPAPER_BORDER_BYTE_ZERO:
*p++ = 0x00;
break;
case REPAPER_BORDER_BYTE_SET:
switch (stage) {
case REPAPER_COMPENSATE:
case REPAPER_WHITE:
case REPAPER_INVERSE:
*p++ = 0x00;
break;
case REPAPER_NORMAL:
*p++ = 0xaa;
break;
}
break;
}
repaper_write_buf(epd->spi, 0x0a, epd->line_buffer,
p - epd->line_buffer);
/* Output data to panel */
repaper_write_val(epd->spi, 0x02, 0x07);
repaper_spi_mosi_low(epd->spi);
}
static void repaper_frame_fixed(struct repaper_epd *epd, u8 fixed_value,
enum repaper_stage stage)
{
unsigned int line;
for (line = 0; line < epd->height; line++)
repaper_one_line(epd, line, NULL, fixed_value, NULL, stage);
}
static void repaper_frame_data(struct repaper_epd *epd, const u8 *image,
const u8 *mask, enum repaper_stage stage)
{
unsigned int line;
if (!mask) {
for (line = 0; line < epd->height; line++) {
repaper_one_line(epd, line,
&image[line * (epd->width / 8)],
0, NULL, stage);
}
} else {
for (line = 0; line < epd->height; line++) {
size_t n = line * epd->width / 8;
repaper_one_line(epd, line, &image[n], 0, &mask[n],
stage);
}
}
}
static void repaper_frame_fixed_repeat(struct repaper_epd *epd, u8 fixed_value,
enum repaper_stage stage)
{
u64 start = local_clock();
u64 end = start + (epd->factored_stage_time * 1000 * 1000);
do {
repaper_frame_fixed(epd, fixed_value, stage);
} while (local_clock() < end);
}
static void repaper_frame_data_repeat(struct repaper_epd *epd, const u8 *image,
const u8 *mask, enum repaper_stage stage)
{
u64 start = local_clock();
u64 end = start + (epd->factored_stage_time * 1000 * 1000);
do {
repaper_frame_data(epd, image, mask, stage);
} while (local_clock() < end);
}
static void repaper_get_temperature(struct repaper_epd *epd)
{
int ret, temperature = 0;
unsigned int factor10x;
if (!epd->thermal)
return;
ret = thermal_zone_get_temp(epd->thermal, &temperature);
if (ret) {
DRM_DEV_ERROR(&epd->spi->dev, "Failed to get temperature (%d)\n", ret);
return;
}
temperature /= 1000;
if (temperature <= -10)
factor10x = 170;
else if (temperature <= -5)
factor10x = 120;
else if (temperature <= 5)
factor10x = 80;
else if (temperature <= 10)
factor10x = 40;
else if (temperature <= 15)
factor10x = 30;
else if (temperature <= 20)
factor10x = 20;
else if (temperature <= 40)
factor10x = 10;
else
factor10x = 7;
epd->factored_stage_time = epd->stage_time * factor10x / 10;
}
static int repaper_fb_dirty(struct drm_framebuffer *fb)
{
drm/gem: rename GEM CMA helpers to GEM DMA helpers Rename "GEM CMA" helpers to "GEM DMA" helpers - considering the hierarchy of APIs (mm/cma -> dma -> gem dma) calling them "GEM DMA" seems to be more applicable. Besides that, commit e57924d4ae80 ("drm/doc: Task to rename CMA helpers") requests to rename the CMA helpers and implies that people seem to be confused about the naming. In order to do this renaming the following script was used: ``` #!/bin/bash DIRS="drivers/gpu include/drm Documentation/gpu" REGEX_SYM_UPPER="[0-9A-Z_\-]" REGEX_SYM_LOWER="[0-9a-z_\-]" REGEX_GREP_UPPER="(${REGEX_SYM_UPPER}*)(GEM)_CMA_(${REGEX_SYM_UPPER}*)" REGEX_GREP_LOWER="(${REGEX_SYM_LOWER}*)(gem)_cma_(${REGEX_SYM_LOWER}*)" REGEX_SED_UPPER="s/${REGEX_GREP_UPPER}/\1\2_DMA_\3/g" REGEX_SED_LOWER="s/${REGEX_GREP_LOWER}/\1\2_dma_\3/g" # Find all upper case 'CMA' symbols and replace them with 'DMA'. for ff in $(grep -REHl "${REGEX_GREP_UPPER}" $DIRS) do sed -i -E "$REGEX_SED_UPPER" $ff done # Find all lower case 'cma' symbols and replace them with 'dma'. for ff in $(grep -REHl "${REGEX_GREP_LOWER}" $DIRS) do sed -i -E "$REGEX_SED_LOWER" $ff done # Replace all occurrences of 'CMA' / 'cma' in comments and # documentation files with 'DMA' / 'dma'. for ff in $(grep -RiHl " cma " $DIRS) do sed -i -E "s/ cma / dma /g" $ff sed -i -E "s/ CMA / DMA /g" $ff done # Rename all 'cma_obj's to 'dma_obj'. for ff in $(grep -RiHl "cma_obj" $DIRS) do sed -i -E "s/cma_obj/dma_obj/g" $ff done ``` Only a few more manual modifications were needed, e.g. reverting the following modifications in some DRM Kconfig files - select CMA if HAVE_DMA_CONTIGUOUS + select DMA if HAVE_DMA_CONTIGUOUS as well as manually picking the occurrences of 'CMA'/'cma' in comments and documentation which relate to "GEM CMA", but not "FB CMA". Also drivers/gpu/drm/Makefile was fixed up manually after renaming drm_gem_cma_helper.c to drm_gem_dma_helper.c. This patch is compile-time tested building a x86_64 kernel with `make allyesconfig && make drivers/gpu/drm`. Acked-by: Sam Ravnborg <sam@ravnborg.org> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Danilo Krummrich <dakr@redhat.com> Reviewed-by: Liviu Dudau <liviu.dudau@arm.com> #drivers/gpu/drm/arm Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Link: https://patchwork.freedesktop.org/patch/msgid/20220802000405.949236-4-dakr@redhat.com
2022-08-02 00:04:03 +00:00
struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
struct repaper_epd *epd = drm_to_epd(fb->dev);
unsigned int dst_pitch = 0;
struct iosys_map dst, vmap;
struct drm_rect clip;
int idx, ret = 0;
u8 *buf = NULL;
if (!drm_dev_enter(fb->dev, &idx))
return -ENODEV;
/* repaper can't do partial updates */
clip.x1 = 0;
clip.x2 = fb->width;
clip.y1 = 0;
clip.y2 = fb->height;
repaper_get_temperature(epd);
DRM_DEBUG("Flushing [FB:%d] st=%ums\n", fb->base.id,
epd->factored_stage_time);
treewide: kmalloc() -> kmalloc_array() The kmalloc() function has a 2-factor argument form, kmalloc_array(). This patch replaces cases of: kmalloc(a * b, gfp) with: kmalloc_array(a * b, gfp) as well as handling cases of: kmalloc(a * b * c, gfp) with: kmalloc(array3_size(a, b, c), gfp) as it's slightly less ugly than: kmalloc_array(array_size(a, b), c, gfp) This does, however, attempt to ignore constant size factors like: kmalloc(4 * 1024, gfp) though any constants defined via macros get caught up in the conversion. Any factors with a sizeof() of "unsigned char", "char", and "u8" were dropped, since they're redundant. The tools/ directory was manually excluded, since it has its own implementation of kmalloc(). The Coccinelle script used for this was: // Fix redundant parens around sizeof(). @@ type TYPE; expression THING, E; @@ ( kmalloc( - (sizeof(TYPE)) * E + sizeof(TYPE) * E , ...) | kmalloc( - (sizeof(THING)) * E + sizeof(THING) * E , ...) ) // Drop single-byte sizes and redundant parens. @@ expression COUNT; typedef u8; typedef __u8; @@ ( kmalloc( - sizeof(u8) * (COUNT) + COUNT , ...) | kmalloc( - sizeof(__u8) * (COUNT) + COUNT , ...) | kmalloc( - sizeof(char) * (COUNT) + COUNT , ...) | kmalloc( - sizeof(unsigned char) * (COUNT) + COUNT , ...) | kmalloc( - sizeof(u8) * COUNT + COUNT , ...) | kmalloc( - sizeof(__u8) * COUNT + COUNT , ...) | kmalloc( - sizeof(char) * COUNT + COUNT , ...) | kmalloc( - sizeof(unsigned char) * COUNT + COUNT , ...) ) // 2-factor product with sizeof(type/expression) and identifier or constant. @@ type TYPE; expression THING; identifier COUNT_ID; constant COUNT_CONST; @@ ( - kmalloc + kmalloc_array ( - sizeof(TYPE) * (COUNT_ID) + COUNT_ID, sizeof(TYPE) , ...) | - kmalloc + kmalloc_array ( - sizeof(TYPE) * COUNT_ID + COUNT_ID, sizeof(TYPE) , ...) | - kmalloc + kmalloc_array ( - sizeof(TYPE) * (COUNT_CONST) + COUNT_CONST, sizeof(TYPE) , ...) | - kmalloc + kmalloc_array ( - sizeof(TYPE) * COUNT_CONST + COUNT_CONST, sizeof(TYPE) , ...) | - kmalloc + kmalloc_array ( - sizeof(THING) * (COUNT_ID) + COUNT_ID, sizeof(THING) , ...) | - kmalloc + kmalloc_array ( - sizeof(THING) * COUNT_ID + COUNT_ID, sizeof(THING) , ...) | - kmalloc + kmalloc_array ( - sizeof(THING) * (COUNT_CONST) + COUNT_CONST, sizeof(THING) , ...) | - kmalloc + kmalloc_array ( - sizeof(THING) * COUNT_CONST + COUNT_CONST, sizeof(THING) , ...) ) // 2-factor product, only identifiers. @@ identifier SIZE, COUNT; @@ - kmalloc + kmalloc_array ( - SIZE * COUNT + COUNT, SIZE , ...) // 3-factor product with 1 sizeof(type) or sizeof(expression), with // redundant parens removed. @@ expression THING; identifier STRIDE, COUNT; type TYPE; @@ ( kmalloc( - sizeof(TYPE) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kmalloc( - sizeof(TYPE) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kmalloc( - sizeof(TYPE) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kmalloc( - sizeof(TYPE) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(TYPE)) , ...) | kmalloc( - sizeof(THING) * (COUNT) * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kmalloc( - sizeof(THING) * (COUNT) * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kmalloc( - sizeof(THING) * COUNT * (STRIDE) + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) | kmalloc( - sizeof(THING) * COUNT * STRIDE + array3_size(COUNT, STRIDE, sizeof(THING)) , ...) ) // 3-factor product with 2 sizeof(variable), with redundant parens removed. @@ expression THING1, THING2; identifier COUNT; type TYPE1, TYPE2; @@ ( kmalloc( - sizeof(TYPE1) * sizeof(TYPE2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | kmalloc( - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2)) , ...) | kmalloc( - sizeof(THING1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | kmalloc( - sizeof(THING1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(THING1), sizeof(THING2)) , ...) | kmalloc( - sizeof(TYPE1) * sizeof(THING2) * COUNT + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) | kmalloc( - sizeof(TYPE1) * sizeof(THING2) * (COUNT) + array3_size(COUNT, sizeof(TYPE1), sizeof(THING2)) , ...) ) // 3-factor product, only identifiers, with redundant parens removed. @@ identifier STRIDE, SIZE, COUNT; @@ ( kmalloc( - (COUNT) * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kmalloc( - COUNT * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kmalloc( - COUNT * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kmalloc( - (COUNT) * (STRIDE) * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) | kmalloc( - COUNT * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kmalloc( - (COUNT) * STRIDE * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kmalloc( - (COUNT) * (STRIDE) * (SIZE) + array3_size(COUNT, STRIDE, SIZE) , ...) | kmalloc( - COUNT * STRIDE * SIZE + array3_size(COUNT, STRIDE, SIZE) , ...) ) // Any remaining multi-factor products, first at least 3-factor products, // when they're not all constants... @@ expression E1, E2, E3; constant C1, C2, C3; @@ ( kmalloc(C1 * C2 * C3, ...) | kmalloc( - (E1) * E2 * E3 + array3_size(E1, E2, E3) , ...) | kmalloc( - (E1) * (E2) * E3 + array3_size(E1, E2, E3) , ...) | kmalloc( - (E1) * (E2) * (E3) + array3_size(E1, E2, E3) , ...) | kmalloc( - E1 * E2 * E3 + array3_size(E1, E2, E3) , ...) ) // And then all remaining 2 factors products when they're not all constants, // keeping sizeof() as the second factor argument. @@ expression THING, E1, E2; type TYPE; constant C1, C2, C3; @@ ( kmalloc(sizeof(THING) * C2, ...) | kmalloc(sizeof(TYPE) * C2, ...) | kmalloc(C1 * C2 * C3, ...) | kmalloc(C1 * C2, ...) | - kmalloc + kmalloc_array ( - sizeof(TYPE) * (E2) + E2, sizeof(TYPE) , ...) | - kmalloc + kmalloc_array ( - sizeof(TYPE) * E2 + E2, sizeof(TYPE) , ...) | - kmalloc + kmalloc_array ( - sizeof(THING) * (E2) + E2, sizeof(THING) , ...) | - kmalloc + kmalloc_array ( - sizeof(THING) * E2 + E2, sizeof(THING) , ...) | - kmalloc + kmalloc_array ( - (E1) * E2 + E1, E2 , ...) | - kmalloc + kmalloc_array ( - (E1) * (E2) + E1, E2 , ...) | - kmalloc + kmalloc_array ( - E1 * E2 + E1, E2 , ...) ) Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 20:55:00 +00:00
buf = kmalloc_array(fb->width, fb->height, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto out_exit;
}
ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
if (ret)
goto out_free;
iosys_map_set_vaddr(&dst, buf);
iosys_map_set_vaddr(&vmap, dma_obj->vaddr);
drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, &vmap, fb, &clip);
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
if (epd->partial) {
repaper_frame_data_repeat(epd, buf, epd->current_frame,
REPAPER_NORMAL);
} else if (epd->cleared) {
repaper_frame_data_repeat(epd, epd->current_frame, NULL,
REPAPER_COMPENSATE);
repaper_frame_data_repeat(epd, epd->current_frame, NULL,
REPAPER_WHITE);
repaper_frame_data_repeat(epd, buf, NULL, REPAPER_INVERSE);
repaper_frame_data_repeat(epd, buf, NULL, REPAPER_NORMAL);
epd->partial = true;
} else {
/* Clear display (anything -> white) */
repaper_frame_fixed_repeat(epd, 0xff, REPAPER_COMPENSATE);
repaper_frame_fixed_repeat(epd, 0xff, REPAPER_WHITE);
repaper_frame_fixed_repeat(epd, 0xaa, REPAPER_INVERSE);
repaper_frame_fixed_repeat(epd, 0xaa, REPAPER_NORMAL);
/* Assuming a clear (white) screen output an image */
repaper_frame_fixed_repeat(epd, 0xaa, REPAPER_COMPENSATE);
repaper_frame_fixed_repeat(epd, 0xaa, REPAPER_WHITE);
repaper_frame_data_repeat(epd, buf, NULL, REPAPER_INVERSE);
repaper_frame_data_repeat(epd, buf, NULL, REPAPER_NORMAL);
epd->cleared = true;
epd->partial = true;
}
memcpy(epd->current_frame, buf, fb->width * fb->height / 8);
/*
* An extra frame write is needed if pixels are set in the bottom line,
* or else grey lines rises up from the pixels
*/
if (epd->pre_border_byte) {
unsigned int x;
for (x = 0; x < (fb->width / 8); x++)
if (buf[x + (fb->width * (fb->height - 1) / 8)]) {
repaper_frame_data_repeat(epd, buf,
epd->current_frame,
REPAPER_NORMAL);
break;
}
}
out_free:
kfree(buf);
out_exit:
drm_dev_exit(idx);
return ret;
}
static void power_off(struct repaper_epd *epd)
{
/* Turn off power and all signals */
gpiod_set_value_cansleep(epd->reset, 0);
gpiod_set_value_cansleep(epd->panel_on, 0);
if (epd->border)
gpiod_set_value_cansleep(epd->border, 0);
/* Ensure SPI MOSI and CLOCK are Low before CS Low */
repaper_spi_mosi_low(epd->spi);
/* Discharge pulse */
gpiod_set_value_cansleep(epd->discharge, 1);
msleep(150);
gpiod_set_value_cansleep(epd->discharge, 0);
}
static enum drm_mode_status repaper_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
const struct drm_display_mode *mode)
{
struct drm_crtc *crtc = &pipe->crtc;
struct repaper_epd *epd = drm_to_epd(crtc->dev);
return drm_crtc_helper_mode_valid_fixed(crtc, mode, epd->mode);
}
static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
struct spi_device *spi = epd->spi;
struct device *dev = &spi->dev;
bool dc_ok = false;
int i, ret, idx;
if (!drm_dev_enter(pipe->crtc.dev, &idx))
return;
DRM_DEBUG_DRIVER("\n");
/* Power up sequence */
gpiod_set_value_cansleep(epd->reset, 0);
gpiod_set_value_cansleep(epd->panel_on, 0);
gpiod_set_value_cansleep(epd->discharge, 0);
if (epd->border)
gpiod_set_value_cansleep(epd->border, 0);
repaper_spi_mosi_low(spi);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(epd->panel_on, 1);
/*
* This delay comes from the repaper.org userspace driver, it's not
* mentioned in the datasheet.
*/
usleep_range(10000, 15000);
gpiod_set_value_cansleep(epd->reset, 1);
if (epd->border)
gpiod_set_value_cansleep(epd->border, 1);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(epd->reset, 0);
usleep_range(5000, 10000);
gpiod_set_value_cansleep(epd->reset, 1);
usleep_range(5000, 10000);
/* Wait for COG to become ready */
for (i = 100; i > 0; i--) {
if (!gpiod_get_value_cansleep(epd->busy))
break;
usleep_range(10, 100);
}
if (!i) {
DRM_DEV_ERROR(dev, "timeout waiting for panel to become ready.\n");
power_off(epd);
goto out_exit;
}
repaper_read_id(spi);
ret = repaper_read_id(spi);
if (ret != REPAPER_RID_G2_COG_ID) {
if (ret < 0)
dev_err(dev, "failed to read chip (%d)\n", ret);
else
dev_err(dev, "wrong COG ID 0x%02x\n", ret);
power_off(epd);
goto out_exit;
}
/* Disable OE */
repaper_write_val(spi, 0x02, 0x40);
ret = repaper_read_val(spi, 0x0f);
if (ret < 0 || !(ret & 0x80)) {
if (ret < 0)
DRM_DEV_ERROR(dev, "failed to read chip (%d)\n", ret);
else
DRM_DEV_ERROR(dev, "panel is reported broken\n");
power_off(epd);
goto out_exit;
}
/* Power saving mode */
repaper_write_val(spi, 0x0b, 0x02);
/* Channel select */
repaper_write_buf(spi, 0x01, epd->channel_select, 8);
/* High power mode osc */
repaper_write_val(spi, 0x07, 0xd1);
/* Power setting */
repaper_write_val(spi, 0x08, 0x02);
/* Vcom level */
repaper_write_val(spi, 0x09, 0xc2);
/* Power setting */
repaper_write_val(spi, 0x04, 0x03);
/* Driver latch on */
repaper_write_val(spi, 0x03, 0x01);
/* Driver latch off */
repaper_write_val(spi, 0x03, 0x00);
usleep_range(5000, 10000);
/* Start chargepump */
for (i = 0; i < 4; ++i) {
/* Charge pump positive voltage on - VGH/VDL on */
repaper_write_val(spi, 0x05, 0x01);
msleep(240);
/* Charge pump negative voltage on - VGL/VDL on */
repaper_write_val(spi, 0x05, 0x03);
msleep(40);
/* Charge pump Vcom on - Vcom driver on */
repaper_write_val(spi, 0x05, 0x0f);
msleep(40);
/* check DC/DC */
ret = repaper_read_val(spi, 0x0f);
if (ret < 0) {
DRM_DEV_ERROR(dev, "failed to read chip (%d)\n", ret);
power_off(epd);
goto out_exit;
}
if (ret & 0x40) {
dc_ok = true;
break;
}
}
if (!dc_ok) {
DRM_DEV_ERROR(dev, "dc/dc failed\n");
power_off(epd);
goto out_exit;
}
/*
* Output enable to disable
* The userspace driver sets this to 0x04, but the datasheet says 0x06
*/
repaper_write_val(spi, 0x02, 0x04);
epd->partial = false;
out_exit:
drm_dev_exit(idx);
}
static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
{
struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
struct spi_device *spi = epd->spi;
unsigned int line;
/*
* This callback is not protected by drm_dev_enter/exit since we want to
* turn off the display on regular driver unload. It's highly unlikely
* that the underlying SPI controller is gone should this be called after
* unplug.
*/
DRM_DEBUG_DRIVER("\n");
/* Nothing frame */
for (line = 0; line < epd->height; line++)
repaper_one_line(epd, 0x7fffu, NULL, 0x00, NULL,
REPAPER_COMPENSATE);
/* 2.7" */
if (epd->border) {
/* Dummy line */
repaper_one_line(epd, 0x7fffu, NULL, 0x00, NULL,
REPAPER_COMPENSATE);
msleep(25);
gpiod_set_value_cansleep(epd->border, 0);
msleep(200);
gpiod_set_value_cansleep(epd->border, 1);
} else {
/* Border dummy line */
repaper_one_line(epd, 0x7fffu, NULL, 0x00, NULL,
REPAPER_NORMAL);
msleep(200);
}
/* not described in datasheet */
repaper_write_val(spi, 0x0b, 0x00);
/* Latch reset turn on */
repaper_write_val(spi, 0x03, 0x01);
/* Power off charge pump Vcom */
repaper_write_val(spi, 0x05, 0x03);
/* Power off charge pump neg voltage */
repaper_write_val(spi, 0x05, 0x01);
msleep(120);
/* Discharge internal */
repaper_write_val(spi, 0x04, 0x80);
/* turn off all charge pumps */
repaper_write_val(spi, 0x05, 0x00);
/* Turn off osc */
repaper_write_val(spi, 0x07, 0x01);
msleep(50);
power_off(epd);
}
static void repaper_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct drm_plane_state *state = pipe->plane.state;
struct drm_rect rect;
if (!pipe->crtc.state->active)
return;
if (drm_atomic_helper_damage_merged(old_state, state, &rect))
repaper_fb_dirty(state->fb);
}
static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
.mode_valid = repaper_pipe_mode_valid,
.enable = repaper_pipe_enable,
.disable = repaper_pipe_disable,
.update = repaper_pipe_update,
};
static int repaper_connector_get_modes(struct drm_connector *connector)
{
struct repaper_epd *epd = drm_to_epd(connector->dev);
return drm_connector_helper_get_modes_fixed(connector, epd->mode);
}
static const struct drm_connector_helper_funcs repaper_connector_hfuncs = {
.get_modes = repaper_connector_get_modes,
};
static const struct drm_connector_funcs repaper_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static const struct drm_mode_config_funcs repaper_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static const uint32_t repaper_formats[] = {
DRM_FORMAT_XRGB8888,
};
static const struct drm_display_mode repaper_e1144cs021_mode = {
DRM_SIMPLE_MODE(128, 96, 29, 22),
};
static const u8 repaper_e1144cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
0x00, 0x0f, 0xff, 0x00 };
static const struct drm_display_mode repaper_e1190cs021_mode = {
DRM_SIMPLE_MODE(144, 128, 36, 32),
};
static const u8 repaper_e1190cs021_cs[] = { 0x00, 0x00, 0x00, 0x03,
0xfc, 0x00, 0x00, 0xff };
static const struct drm_display_mode repaper_e2200cs021_mode = {
DRM_SIMPLE_MODE(200, 96, 46, 22),
};
static const u8 repaper_e2200cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
0x01, 0xff, 0xe0, 0x00 };
static const struct drm_display_mode repaper_e2271cs021_mode = {
DRM_SIMPLE_MODE(264, 176, 57, 38),
};
static const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f,
0xff, 0xfe, 0x00, 0x00 };
drm/gem: rename GEM CMA helpers to GEM DMA helpers Rename "GEM CMA" helpers to "GEM DMA" helpers - considering the hierarchy of APIs (mm/cma -> dma -> gem dma) calling them "GEM DMA" seems to be more applicable. Besides that, commit e57924d4ae80 ("drm/doc: Task to rename CMA helpers") requests to rename the CMA helpers and implies that people seem to be confused about the naming. In order to do this renaming the following script was used: ``` #!/bin/bash DIRS="drivers/gpu include/drm Documentation/gpu" REGEX_SYM_UPPER="[0-9A-Z_\-]" REGEX_SYM_LOWER="[0-9a-z_\-]" REGEX_GREP_UPPER="(${REGEX_SYM_UPPER}*)(GEM)_CMA_(${REGEX_SYM_UPPER}*)" REGEX_GREP_LOWER="(${REGEX_SYM_LOWER}*)(gem)_cma_(${REGEX_SYM_LOWER}*)" REGEX_SED_UPPER="s/${REGEX_GREP_UPPER}/\1\2_DMA_\3/g" REGEX_SED_LOWER="s/${REGEX_GREP_LOWER}/\1\2_dma_\3/g" # Find all upper case 'CMA' symbols and replace them with 'DMA'. for ff in $(grep -REHl "${REGEX_GREP_UPPER}" $DIRS) do sed -i -E "$REGEX_SED_UPPER" $ff done # Find all lower case 'cma' symbols and replace them with 'dma'. for ff in $(grep -REHl "${REGEX_GREP_LOWER}" $DIRS) do sed -i -E "$REGEX_SED_LOWER" $ff done # Replace all occurrences of 'CMA' / 'cma' in comments and # documentation files with 'DMA' / 'dma'. for ff in $(grep -RiHl " cma " $DIRS) do sed -i -E "s/ cma / dma /g" $ff sed -i -E "s/ CMA / DMA /g" $ff done # Rename all 'cma_obj's to 'dma_obj'. for ff in $(grep -RiHl "cma_obj" $DIRS) do sed -i -E "s/cma_obj/dma_obj/g" $ff done ``` Only a few more manual modifications were needed, e.g. reverting the following modifications in some DRM Kconfig files - select CMA if HAVE_DMA_CONTIGUOUS + select DMA if HAVE_DMA_CONTIGUOUS as well as manually picking the occurrences of 'CMA'/'cma' in comments and documentation which relate to "GEM CMA", but not "FB CMA". Also drivers/gpu/drm/Makefile was fixed up manually after renaming drm_gem_cma_helper.c to drm_gem_dma_helper.c. This patch is compile-time tested building a x86_64 kernel with `make allyesconfig && make drivers/gpu/drm`. Acked-by: Sam Ravnborg <sam@ravnborg.org> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Danilo Krummrich <dakr@redhat.com> Reviewed-by: Liviu Dudau <liviu.dudau@arm.com> #drivers/gpu/drm/arm Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Link: https://patchwork.freedesktop.org/patch/msgid/20220802000405.949236-4-dakr@redhat.com
2022-08-02 00:04:03 +00:00
DEFINE_DRM_GEM_DMA_FOPS(repaper_fops);
drm/<drivers>: Constify struct drm_driver Only the following drivers aren't converted: - amdgpu, because of the driver_feature mangling due to virt support. Subsequent patch will address this. - nouveau, because DRIVER_ATOMIC uapi is still not the default on the platforms where it's supported (i.e. again driver_feature mangling) - vc4, again because of driver_feature mangling - qxl, because the ioctl table is somewhere else and moving that is maybe a bit too much, hence the num_ioctls assignment prevents a const driver structure. - arcpgu, because that is stuck behind a pending tiny-fication series from me. - legacy drivers, because legacy requires non-const drm_driver. Note that for armada I also went ahead and made the ioctl array const. Only cc'ing the driver people who've not been converted (everyone else is way too much). v2: Fix one misplaced const static, should be static const (0day) v3: - Improve commit message (Sam) Acked-by: Sam Ravnborg <sam@ravnborg.org> Cc: kernel test robot <lkp@intel.com> Acked-by: Maxime Ripard <mripard@kernel.org> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Cc: Sam Ravnborg <sam@ravnborg.org> Cc: Dave Airlie <airlied@redhat.com> Cc: Gerd Hoffmann <kraxel@redhat.com> Cc: virtualization@lists.linux-foundation.org Cc: Harry Wentland <harry.wentland@amd.com> Cc: Leo Li <sunpeng.li@amd.com> Cc: Alex Deucher <alexander.deucher@amd.com> Cc: Christian König <christian.koenig@amd.com> Cc: Eric Anholt <eric@anholt.net> Cc: Maxime Ripard <mripard@kernel.org> Cc: Ben Skeggs <bskeggs@redhat.com> Cc: nouveau@lists.freedesktop.org Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20201104100425.1922351-5-daniel.vetter@ffwll.ch
2020-11-04 10:04:24 +00:00
static const struct drm_driver repaper_driver = {
drm/prime: Actually remove DRIVER_PRIME everywhere Split out to make the functional changes stick out more. All places where DRIVER_PRIME was used have been removed in previous patches already. v2: amdgpu gained DRIVER_SYNCOBJ_TIMELINE. v3: amdgpu lost DRIVER_SYNCOBJ_TIMELINE. v4: Don't add a space in i915_drv.c (Sam) v5: Add note that previous patches removed all the DRIVER_PRIME users already (Emil). v6: Fixupe ingenic (new driver) while applying. Cc: Sam Ravnborg <sam@ravnborg.org> Reviewed-by: Emil Velikov <emil.velikov@collabora.com> Reviewed-by: Eric Anholt <eric@anholt.net> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> Cc: amd-gfx@lists.freedesktop.org Cc: etnaviv@lists.freedesktop.org Cc: freedreno@lists.freedesktop.org Cc: intel-gfx@lists.freedesktop.org Cc: lima@lists.freedesktop.org Cc: linux-amlogic@lists.infradead.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-arm-msm@vger.kernel.org Cc: linux-aspeed@lists.ozlabs.org Cc: linux-renesas-soc@vger.kernel.org Cc: linux-rockchip@lists.infradead.org Cc: linux-samsung-soc@vger.kernel.org Cc: linux-stm32@st-md-mailman.stormreply.com Cc: linux-tegra@vger.kernel.org Cc: nouveau@lists.freedesktop.org Cc: NXP Linux Team <linux-imx@nxp.com> Cc: spice-devel@lists.freedesktop.org Cc: virtualization@lists.linux-foundation.org Cc: VMware Graphics <linux-graphics-maintainer@vmware.com> Cc: xen-devel@lists.xenproject.org Link: https://patchwork.freedesktop.org/patch/msgid/20190617153924.414-1-daniel.vetter@ffwll.ch
2019-06-17 15:39:24 +00:00
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &repaper_fops,
drm/gem: rename GEM CMA helpers to GEM DMA helpers Rename "GEM CMA" helpers to "GEM DMA" helpers - considering the hierarchy of APIs (mm/cma -> dma -> gem dma) calling them "GEM DMA" seems to be more applicable. Besides that, commit e57924d4ae80 ("drm/doc: Task to rename CMA helpers") requests to rename the CMA helpers and implies that people seem to be confused about the naming. In order to do this renaming the following script was used: ``` #!/bin/bash DIRS="drivers/gpu include/drm Documentation/gpu" REGEX_SYM_UPPER="[0-9A-Z_\-]" REGEX_SYM_LOWER="[0-9a-z_\-]" REGEX_GREP_UPPER="(${REGEX_SYM_UPPER}*)(GEM)_CMA_(${REGEX_SYM_UPPER}*)" REGEX_GREP_LOWER="(${REGEX_SYM_LOWER}*)(gem)_cma_(${REGEX_SYM_LOWER}*)" REGEX_SED_UPPER="s/${REGEX_GREP_UPPER}/\1\2_DMA_\3/g" REGEX_SED_LOWER="s/${REGEX_GREP_LOWER}/\1\2_dma_\3/g" # Find all upper case 'CMA' symbols and replace them with 'DMA'. for ff in $(grep -REHl "${REGEX_GREP_UPPER}" $DIRS) do sed -i -E "$REGEX_SED_UPPER" $ff done # Find all lower case 'cma' symbols and replace them with 'dma'. for ff in $(grep -REHl "${REGEX_GREP_LOWER}" $DIRS) do sed -i -E "$REGEX_SED_LOWER" $ff done # Replace all occurrences of 'CMA' / 'cma' in comments and # documentation files with 'DMA' / 'dma'. for ff in $(grep -RiHl " cma " $DIRS) do sed -i -E "s/ cma / dma /g" $ff sed -i -E "s/ CMA / DMA /g" $ff done # Rename all 'cma_obj's to 'dma_obj'. for ff in $(grep -RiHl "cma_obj" $DIRS) do sed -i -E "s/cma_obj/dma_obj/g" $ff done ``` Only a few more manual modifications were needed, e.g. reverting the following modifications in some DRM Kconfig files - select CMA if HAVE_DMA_CONTIGUOUS + select DMA if HAVE_DMA_CONTIGUOUS as well as manually picking the occurrences of 'CMA'/'cma' in comments and documentation which relate to "GEM CMA", but not "FB CMA". Also drivers/gpu/drm/Makefile was fixed up manually after renaming drm_gem_cma_helper.c to drm_gem_dma_helper.c. This patch is compile-time tested building a x86_64 kernel with `make allyesconfig && make drivers/gpu/drm`. Acked-by: Sam Ravnborg <sam@ravnborg.org> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Danilo Krummrich <dakr@redhat.com> Reviewed-by: Liviu Dudau <liviu.dudau@arm.com> #drivers/gpu/drm/arm Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Link: https://patchwork.freedesktop.org/patch/msgid/20220802000405.949236-4-dakr@redhat.com
2022-08-02 00:04:03 +00:00
DRM_GEM_DMA_DRIVER_OPS_VMAP,
.name = "repaper",
.desc = "Pervasive Displays RePaper e-ink panels",
.date = "20170405",
.major = 1,
.minor = 0,
};
static const struct of_device_id repaper_of_match[] = {
{ .compatible = "pervasive,e1144cs021", .data = (void *)E1144CS021 },
{ .compatible = "pervasive,e1190cs021", .data = (void *)E1190CS021 },
{ .compatible = "pervasive,e2200cs021", .data = (void *)E2200CS021 },
{ .compatible = "pervasive,e2271cs021", .data = (void *)E2271CS021 },
{},
};
MODULE_DEVICE_TABLE(of, repaper_of_match);
static const struct spi_device_id repaper_id[] = {
{ "e1144cs021", E1144CS021 },
{ "e1190cs021", E1190CS021 },
{ "e2200cs021", E2200CS021 },
{ "e2271cs021", E2271CS021 },
{ },
};
MODULE_DEVICE_TABLE(spi, repaper_id);
static int repaper_probe(struct spi_device *spi)
{
const struct drm_display_mode *mode;
const struct spi_device_id *spi_id;
struct device *dev = &spi->dev;
enum repaper_model model;
const char *thermal_zone;
struct repaper_epd *epd;
size_t line_buffer_size;
struct drm_device *drm;
const void *match;
int ret;
match = device_get_match_data(dev);
if (match) {
model = (enum repaper_model)match;
} else {
spi_id = spi_get_device_id(spi);
model = (enum repaper_model)spi_id->driver_data;
}
/* The SPI device is used to allocate dma memory */
if (!dev->coherent_dma_mask) {
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_warn(dev, "Failed to set dma mask %d\n", ret);
return ret;
}
}
epd = devm_drm_dev_alloc(dev, &repaper_driver,
struct repaper_epd, drm);
if (IS_ERR(epd))
return PTR_ERR(epd);
drm = &epd->drm;
ret = drmm_mode_config_init(drm);
if (ret)
return ret;
drm->mode_config.funcs = &repaper_mode_config_funcs;
epd->spi = spi;
epd->panel_on = devm_gpiod_get(dev, "panel-on", GPIOD_OUT_LOW);
if (IS_ERR(epd->panel_on)) {
ret = PTR_ERR(epd->panel_on);
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'panel-on'\n");
return ret;
}
epd->discharge = devm_gpiod_get(dev, "discharge", GPIOD_OUT_LOW);
if (IS_ERR(epd->discharge)) {
ret = PTR_ERR(epd->discharge);
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'discharge'\n");
return ret;
}
epd->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(epd->reset)) {
ret = PTR_ERR(epd->reset);
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
return ret;
}
epd->busy = devm_gpiod_get(dev, "busy", GPIOD_IN);
if (IS_ERR(epd->busy)) {
ret = PTR_ERR(epd->busy);
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'busy'\n");
return ret;
}
if (!device_property_read_string(dev, "pervasive,thermal-zone",
&thermal_zone)) {
epd->thermal = thermal_zone_get_zone_by_name(thermal_zone);
if (IS_ERR(epd->thermal)) {
DRM_DEV_ERROR(dev, "Failed to get thermal zone: %s\n", thermal_zone);
return PTR_ERR(epd->thermal);
}
}
switch (model) {
case E1144CS021:
mode = &repaper_e1144cs021_mode;
epd->channel_select = repaper_e1144cs021_cs;
epd->stage_time = 480;
epd->bytes_per_scan = 96 / 4;
epd->middle_scan = true; /* data-scan-data */
epd->pre_border_byte = false;
epd->border_byte = REPAPER_BORDER_BYTE_ZERO;
break;
case E1190CS021:
mode = &repaper_e1190cs021_mode;
epd->channel_select = repaper_e1190cs021_cs;
epd->stage_time = 480;
epd->bytes_per_scan = 128 / 4 / 2;
epd->middle_scan = false; /* scan-data-scan */
epd->pre_border_byte = false;
epd->border_byte = REPAPER_BORDER_BYTE_SET;
break;
case E2200CS021:
mode = &repaper_e2200cs021_mode;
epd->channel_select = repaper_e2200cs021_cs;
epd->stage_time = 480;
epd->bytes_per_scan = 96 / 4;
epd->middle_scan = true; /* data-scan-data */
epd->pre_border_byte = true;
epd->border_byte = REPAPER_BORDER_BYTE_NONE;
break;
case E2271CS021:
epd->border = devm_gpiod_get(dev, "border", GPIOD_OUT_LOW);
if (IS_ERR(epd->border)) {
ret = PTR_ERR(epd->border);
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "Failed to get gpio 'border'\n");
return ret;
}
mode = &repaper_e2271cs021_mode;
epd->channel_select = repaper_e2271cs021_cs;
epd->stage_time = 630;
epd->bytes_per_scan = 176 / 4;
epd->middle_scan = true; /* data-scan-data */
epd->pre_border_byte = true;
epd->border_byte = REPAPER_BORDER_BYTE_NONE;
break;
default:
return -ENODEV;
}
epd->mode = mode;
epd->width = mode->hdisplay;
epd->height = mode->vdisplay;
epd->factored_stage_time = epd->stage_time;
line_buffer_size = 2 * epd->width / 8 + epd->bytes_per_scan + 2;
epd->line_buffer = devm_kzalloc(dev, line_buffer_size, GFP_KERNEL);
if (!epd->line_buffer)
return -ENOMEM;
epd->current_frame = devm_kzalloc(dev, epd->width * epd->height / 8,
GFP_KERNEL);
if (!epd->current_frame)
return -ENOMEM;
drm->mode_config.min_width = mode->hdisplay;
drm->mode_config.max_width = mode->hdisplay;
drm->mode_config.min_height = mode->vdisplay;
drm->mode_config.max_height = mode->vdisplay;
drm_connector_helper_add(&epd->connector, &repaper_connector_hfuncs);
ret = drm_connector_init(drm, &epd->connector, &repaper_connector_funcs,
DRM_MODE_CONNECTOR_SPI);
if (ret)
return ret;
ret = drm_simple_display_pipe_init(drm, &epd->pipe, &repaper_pipe_funcs,
repaper_formats, ARRAY_SIZE(repaper_formats),
NULL, &epd->connector);
if (ret)
return ret;
drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0);
if (ret)
return ret;
spi_set_drvdata(spi, drm);
DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
drm_fbdev_generic_setup(drm, 0);
return 0;
}
static void repaper_remove(struct spi_device *spi)
{
struct drm_device *drm = spi_get_drvdata(spi);
drm_dev_unplug(drm);
drm_atomic_helper_shutdown(drm);
}
static void repaper_shutdown(struct spi_device *spi)
{
drm_atomic_helper_shutdown(spi_get_drvdata(spi));
}
static struct spi_driver repaper_spi_driver = {
.driver = {
.name = "repaper",
.of_match_table = repaper_of_match,
},
.id_table = repaper_id,
.probe = repaper_probe,
.remove = repaper_remove,
.shutdown = repaper_shutdown,
};
module_spi_driver(repaper_spi_driver);
MODULE_DESCRIPTION("Pervasive Displays RePaper DRM driver");
MODULE_AUTHOR("Noralf Trønnes");
MODULE_LICENSE("GPL");