Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions drivers/gpu/drm/drm_client_modeset.c
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,14 @@ static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool
plane_state->rotation = rotation;
}

/* Apply fbdev rotation override if set */
if (client->fbdev_rotation) {
struct drm_plane_state *plane_state;

plane_state = drm_atomic_get_new_plane_state(state, primary);
plane_state->rotation = client->fbdev_rotation;
}

ret = __drm_atomic_helper_set_config(mode_set, state);
if (ret != 0)
goto out_state;
Expand Down
3 changes: 3 additions & 0 deletions drivers/gpu/drm/drm_fb_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,9 @@ int drm_fb_helper_set_par(struct fb_info *info)
*/
force = var->activate & FB_ACTIVATE_KD_TEXT;

/* Translate fbdev rotation to DRM rotation property */
fb_helper->client.fbdev_rotation = 1 << var->rotate;

__drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);

return 0;
Expand Down
116 changes: 96 additions & 20 deletions drivers/gpu/drm/mxc-epdc/epdc_update.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <drm/drm_blend.h>
#include <drm/drm_rect.h>
#include <media/pxp.h>
#include <asm/cacheflush.h>
#include "mxc_epdc.h"
#include "epdc_hw.h"
Expand Down Expand Up @@ -335,17 +338,40 @@ static int epdc_submit_merge(struct update_desc_list *upd_desc_list,
return MERGE_OK;
}

static void epdc_from_rgb_clear_lower_nibble(struct drm_rect *clip, void *vaddr, int pitch, u8 *dst, int dst_pitch)
/* Transform coordinates based on rotation */
static void transform_coords(unsigned int x, unsigned int y,
u32 width, u32 height, unsigned int rotation,
unsigned int *out_x, unsigned int *out_y)
{
unsigned int x, y;
switch (rotation) {
case DRM_MODE_ROTATE_90:
*out_x = height - 1 - y;
*out_y = x;
break;
case DRM_MODE_ROTATE_180:
*out_x = width - 1 - x;
*out_y = height - 1 - y;
break;
case DRM_MODE_ROTATE_270:
*out_x = y;
*out_y = width - 1 - x;
break;
default:
*out_x = x;
*out_y = y;
break;
}
}

dst += clip->y1 * dst_pitch;
static void epdc_from_rgb_clear_lower_nibble(struct drm_rect *clip, void *vaddr, int pitch, u8 *dst, int dst_pitch,
u32 fb_width, u32 fb_height, unsigned int rotation)
{
unsigned int x, y;

for (y = clip->y1; y < clip->y2; y++, dst += dst_pitch) {
u32 *src;
src = vaddr + (y * pitch);
src += clip->x1;
for (y = clip->y1; y < clip->y2; y++) {
u32 *src = vaddr + (y * pitch) + clip->x1;
for (x = clip->x1; x < clip->x2; x++) {
unsigned int out_x, out_y;
u8 r = (*src & 0x00ff0000) >> 16;
u8 g = (*src & 0x0000ff00) >> 8;
u8 b = *src & 0x000000ff;
Expand All @@ -355,34 +381,35 @@ static void epdc_from_rgb_clear_lower_nibble(struct drm_rect *clip, void *vaddr,

/*
* done in Tolino 3.0.x kernels via PXP_LUT_AA
* needed for 5 bit waveforms
* needed for 5 bit waveforms
*/

dst[x] = gray & 0xF0;
transform_coords(x, y, fb_width, fb_height, rotation, &out_x, &out_y);
dst[out_y * dst_pitch + out_x] = gray & 0xF0;
src++;
}
}
}

/* found by experimentation, reduced number of levels of gray */
static void epdc_from_rgb_shift(struct drm_rect *clip, void *vaddr, int pitch, u8 *dst, int dst_pitch)
static void epdc_from_rgb_shift(struct drm_rect *clip, void *vaddr, int pitch, u8 *dst, int dst_pitch,
u32 fb_width, u32 fb_height, unsigned int rotation)
{
unsigned int x, y;

dst += clip->y1 * dst_pitch;

for (y = clip->y1; y < clip->y2; y++, dst += dst_pitch) {
u32 *src;
src = vaddr + (y * pitch);
src += clip->x1;
for (y = clip->y1; y < clip->y2; y++) {
u32 *src = vaddr + (y * pitch) + clip->x1;
for (x = clip->x1; x < clip->x2; x++) {
unsigned int out_x, out_y;
u8 r = (*src & 0x00ff0000) >> 16;
u8 g = (*src & 0x0000ff00) >> 8;
u8 b = *src & 0x000000ff;

/* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */
u8 gray = (3 * r + 6 * g + b) / 10;
dst[x] = (gray >> 2) | 0xC0;

transform_coords(x, y, fb_width, fb_height, rotation, &out_x, &out_y);
dst[out_y * dst_pitch + out_x] = (gray >> 2) | 0xC0;
src++;
}
}
Expand Down Expand Up @@ -843,19 +870,67 @@ void mxc_epdc_draw_mode0(struct mxc_epdc *priv)
}


int mxc_epdc_send_single_update(struct drm_rect *clip, int pitch, void *vaddr,
int mxc_epdc_send_single_update(struct drm_rect *clip, int pitch, dma_addr_t src_addr, void *vaddr,
struct mxc_epdc *priv)
{
struct update_desc_list *upd_desc;

unsigned int rotation = priv->rotation;
bool rotate_90_270 = drm_rotation_90_or_270(rotation);
u32 fb_width, fb_height;
struct pxp_epdc_config pxp_cfg;
enum pxp_grayscale_mode pxp_mode;
struct device *pxp_dev;
int ret;

/* Framebuffer dimensions (before rotation) */
if (rotate_90_270) {
fb_width = priv->epdc_mem_height;
fb_height = priv->epdc_mem_width;
} else {
fb_width = priv->epdc_mem_width;
fb_height = priv->epdc_mem_height;
}

/* Try to use PxP for hardware processing */
pxp_dev = pxp_get_device();
if (pxp_dev) {
/* Determine grayscale mode for EPDC */
if ((priv->rev < 30) || (priv->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P4N))
pxp_mode = PXP_GRAYSCALE_Y4_UPPER;
else
pxp_mode = PXP_GRAYSCALE_Y8;

/* Configure PxP for this update */
pxp_cfg.src_addr = src_addr;
pxp_cfg.dst_addr = priv->epdc_mem_phys;
pxp_cfg.src_width = fb_width;
pxp_cfg.src_height = fb_height;
pxp_cfg.src_stride = pitch;
pxp_cfg.dst_stride = priv->epdc_mem_width;
pxp_cfg.rotation = rotation;
pxp_cfg.clip = clip;

ret = pxp_epdc_process(pxp_dev, &pxp_cfg, pxp_mode);
if (ret == 0)
goto pxp_done; /* PxP succeeded, skip software copy */
/* PxP failed, fall back to software */
dev_dbg(priv->drm.dev, "PxP processing failed, using software fallback\n");
}

/* Software fallback for pixel processing */
/* I'm not really certain if this is still needed on some devices */
if ((priv->rev < 30) || (priv->buf_pix_fmt == EPDC_FORMAT_BUF_PIXEL_FORMAT_P4N))
epdc_from_rgb_clear_lower_nibble(clip, vaddr, pitch,
(u8 *)priv->epdc_mem_virt,
priv->epdc_mem_width);
priv->epdc_mem_width,
fb_width, fb_height, rotation);
else
epdc_from_rgb_shift(clip, vaddr, pitch,
(u8 *)priv->epdc_mem_virt,
priv->epdc_mem_width);
priv->epdc_mem_width,
fb_width, fb_height, rotation);
pxp_done:

/* Has EPDC HW been initialized? */
if (!priv->hw_ready) {
Expand Down Expand Up @@ -885,6 +960,7 @@ int mxc_epdc_send_single_update(struct drm_rect *clip, int pitch, void *vaddr,
}
/* Initialize per-update marker list */
upd_desc->upd_data.update_region = *clip;
drm_rect_rotate(&upd_desc->upd_data.update_region, fb_width, fb_height, rotation);
upd_desc->upd_data.waveform_mode = WAVEFORM_MODE_AUTO;
upd_desc->upd_data.temp = TEMP_USE_AMBIENT;
upd_desc->upd_data.update_mode = UPDATE_MODE_PARTIAL;
Expand Down
6 changes: 4 additions & 2 deletions drivers/gpu/drm/mxc-epdc/epdc_update.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* Copyright (C) 2020 Andreas Kemnade */
#include <linux/types.h>

int mxc_epdc_send_single_update(struct drm_rect *clip, int pitch,
void *vaddr,
struct mxc_epdc *priv);
dma_addr_t src_addr, void *vaddr,
struct mxc_epdc *priv);
void mxc_epdc_draw_mode0(struct mxc_epdc *priv);
int mxc_epdc_init_update(struct mxc_epdc *priv);
void mxc_epdc_flush_updates(struct mxc_epdc *priv);
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/mxc-epdc/mxc_epdc.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ struct mxc_epdc {
struct drm_connector connector;
struct display_timing timing;
struct imx_epdc_fb_mode imx_mode;
unsigned int rotation;
void __iomem *iobase;
struct completion powerdown_compl;
struct clk *epdc_clk_axi;
Expand Down
37 changes: 32 additions & 5 deletions drivers/gpu/drm/mxc-epdc/mxc_epdc_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fbdev_ttm.h>
#include <media/pxp.h>
#include <drm/drm_file.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_framebuffer.h>
Expand All @@ -29,6 +30,7 @@
#include <drm/drm_panel.h>
#include <drm/drm_prime.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_blend.h>
#include "mxc_epdc.h"
#include "epdc_hw.h"
#include "epdc_update.h"
Expand Down Expand Up @@ -189,10 +191,26 @@ static void mxc_epdc_pipe_enable(struct drm_simple_display_pipe *pipe,
{
struct mxc_epdc *priv = drm_pipe_to_mxc_epdc(pipe);
struct drm_display_mode *m = &pipe->crtc.state->adjusted_mode;
unsigned int rotation = plane_state->rotation;
bool rotate_90_270 = drm_rotation_90_or_270(rotation);
u32 width, height;

dev_info(priv->drm.dev, "Mode: %d x %d, rotation: %u\n",
m->hdisplay, m->vdisplay, rotation);

priv->rotation = rotation;

/* Swap dimensions for 90/270 degree rotation */
if (rotate_90_270) {
width = m->vdisplay;
height = m->hdisplay;
} else {
width = m->hdisplay;
height = m->vdisplay;
}

dev_info(priv->drm.dev, "Mode: %d x %d\n", m->hdisplay, m->vdisplay);
priv->epdc_mem_width = m->hdisplay;
priv->epdc_mem_height = m->vdisplay;
priv->epdc_mem_width = width;
priv->epdc_mem_height = height;
priv->epdc_mem_virt = dma_alloc_wc(priv->drm.dev,
m->hdisplay * m->vdisplay,
&priv->epdc_mem_phys, GFP_DMA | GFP_KERNEL);
Expand All @@ -214,7 +232,7 @@ static void mxc_epdc_pipe_enable(struct drm_simple_display_pipe *pipe,
gem = drm_fb_dma_get_gem_obj(plane_state->fb, 0);
mxc_epdc_send_single_update(&clip,
plane_state->fb->pitches[0],
gem->vaddr, priv);
gem->dma_addr, gem->vaddr, priv);
}

}
Expand Down Expand Up @@ -266,7 +284,7 @@ static void mxc_epdc_pipe_update(struct drm_simple_display_pipe *pipe,
clip.x1, clip.y1, clip.x2, clip.y2);

mxc_epdc_send_single_update(&clip, old_state->fb->pitches[0],
gem->vaddr, priv);
gem->dma_addr, gem->vaddr, priv);
}

return;
Expand Down Expand Up @@ -354,6 +372,15 @@ static int mxc_epdc_probe(struct platform_device *pdev)
ARRAY_SIZE(mxc_epdc_formats),
NULL,
&priv->connector);

/* Add rotation property to support display rotation */
drm_plane_create_rotation_property(&priv->pipe.plane,
DRM_MODE_ROTATE_0,
DRM_MODE_ROTATE_0 |
DRM_MODE_ROTATE_90 |
DRM_MODE_ROTATE_180 |
DRM_MODE_ROTATE_270);

drm_plane_enable_fb_damage_clips(&priv->pipe.plane);

drm_mode_config_reset(&priv->drm);
Expand Down
Loading