Commit 5c8c397c authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm-misc-next-2019-04-10' of git://anongit.freedesktop.org/drm/drm-misc into drm-next

drm-misc-next for 5.2:

UAPI Changes:
- None

Cross-subsystem Changes:
-MAINTAINERS: Add moderation flag for lima mailing list (Randy)
-dt-bindings: Add Mali Bifrost bindings (Neil)
-dt-bindings: Add G12A compatibility strings to meson bindings (Neil)

Core Changes:
-Add a handful of format helpers (Gerd)

Driver Changes:
-cirrus: Driver rewrite megapatch (Gerd)
-meson: Add G12A support to meson driver (Neil)
-lima: Couple fixes (Qiang)

Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Neil Armstrong <narmstrong@baylibre.com>
Cc: Qiang Yu <yuq825@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Sean Paul <sean@poorly.run>
Link: https://patchwork.freedesktop.org/patch/msgid/20190410194907.GA108842@art_vandelay
parents 14d2bd53 80bb8d98
......@@ -37,6 +37,7 @@ Required properties:
- GXL (S905X, S905D) : "amlogic,meson-gxl-dw-hdmi"
- GXM (S912) : "amlogic,meson-gxm-dw-hdmi"
followed by the common "amlogic,meson-gx-dw-hdmi"
- G12A (S905X2, S905Y2, S905D2) : "amlogic,meson-g12a-dw-hdmi"
- reg: Physical base address and length of the controller's registers.
- interrupts: The HDMI interrupt number
- clocks, clock-names : must have the phandles to the HDMI iahb and isfr clocks,
......@@ -66,6 +67,9 @@ corresponding to each HDMI output and input.
S905X (GXL) VENC Input TMDS Output
S905D (GXL) VENC Input TMDS Output
S912 (GXM) VENC Input TMDS Output
S905X2 (G12A) VENC Input TMDS Output
S905Y2 (G12A) VENC Input TMDS Output
S905D2 (G12A) VENC Input TMDS Output
Example:
......
......@@ -57,6 +57,7 @@ Required properties:
- GXL (S905X, S905D) : "amlogic,meson-gxl-vpu"
- GXM (S912) : "amlogic,meson-gxm-vpu"
followed by the common "amlogic,meson-gx-vpu"
- G12A (S905X2, S905Y2, S905D2) : "amlogic,meson-g12a-vpu"
- reg: base address and size of he following memory-mapped regions :
- vpu
- hhi
......@@ -83,6 +84,9 @@ corresponding to each VPU output.
S905X (GXL) CVBS VDAC HDMI-TX
S905D (GXL) CVBS VDAC HDMI-TX
S912 (GXM) CVBS VDAC HDMI-TX
S905X2 (G12A) CVBS VDAC HDMI-TX
S905Y2 (G12A) CVBS VDAC HDMI-TX
S905D2 (G12A) CVBS VDAC HDMI-TX
Example:
......
ARM Mali Bifrost GPU
====================
Required properties:
- compatible :
* Since Mali Bifrost GPU model/revision is fully discoverable by reading
some determined registers, must contain the following:
+ "arm,mali-bifrost"
* which must be preceded by one of the following vendor specifics:
+ "amlogic,meson-g12a-mali"
- reg : Physical base address of the device and length of the register area.
- interrupts : Contains the three IRQ lines required by Mali Bifrost devices,
in the following defined order.
- interrupt-names : Contains the names of IRQ resources in this exact defined
order: "job", "mmu", "gpu".
Optional properties:
- clocks : Phandle to clock for the Mali Bifrost device.
- mali-supply : Phandle to regulator for the Mali device. Refer to
Documentation/devicetree/bindings/regulator/regulator.txt for details.
- operating-points-v2 : Refer to Documentation/devicetree/bindings/opp/opp.txt
for details.
- resets : Phandle of the GPU reset line.
Vendor-specific bindings
------------------------
The Mali GPU is integrated very differently from one SoC to
another. In order to accommodate those differences, you have the option
to specify one more vendor-specific compatible, among:
- "amlogic,meson-g12a-mali"
Required properties:
- resets : Should contain phandles of :
+ GPU reset line
+ GPU APB glue reset line
Example for a Mali-G31:
gpu@ffa30000 {
compatible = "amlogic,meson-g12a-mali", "arm,mali-bifrost";
reg = <0xffe40000 0x10000>;
interrupts = <GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "job", "mmu", "gpu";
clocks = <&clk CLKID_MALI>;
mali-supply = <&vdd_gpu>;
operating-points-v2 = <&gpu_opp_table>;
resets = <&reset RESET_DVALIN_CAPB3>, <&reset RESET_DVALIN>;
};
gpu_opp_table: opp_table0 {
compatible = "operating-points-v2";
opp@533000000 {
opp-hz = /bits/ 64 <533000000>;
opp-microvolt = <1250000>;
};
opp@450000000 {
opp-hz = /bits/ 64 <450000000>;
opp-microvolt = <1150000>;
};
opp@400000000 {
opp-hz = /bits/ 64 <400000000>;
opp-microvolt = <1125000>;
};
opp@350000000 {
opp-hz = /bits/ 64 <350000000>;
opp-microvolt = <1075000>;
};
opp@266000000 {
opp-hz = /bits/ 64 <266000000>;
opp-microvolt = <1025000>;
};
opp@160000000 {
opp-hz = /bits/ 64 <160000000>;
opp-microvolt = <925000>;
};
opp@100000000 {
opp-hz = /bits/ 64 <100000000>;
opp-microvolt = <912500>;
};
};
......@@ -42,12 +42,6 @@ Video Encoder
.. kernel-doc:: drivers/gpu/drm/meson/meson_venc.c
:doc: Video Encoder
Video Canvas Management
=======================
.. kernel-doc:: drivers/gpu/drm/meson/meson_canvas.c
:doc: Canvas
Video Clocks
============
......
......@@ -5209,7 +5209,7 @@ F: Documentation/devicetree/bindings/display/hisilicon/
DRM DRIVERS FOR LIMA
M: Qiang Yu <yuq825@gmail.com>
L: dri-devel@lists.freedesktop.org
L: lima@lists.freedesktop.org
L: lima@lists.freedesktop.org (moderated for non-subscribers)
S: Maintained
F: drivers/gpu/drm/lima/
F: include/uapi/drm/lima_drm.h
......
......@@ -38,7 +38,8 @@ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper
drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
drm_simple_kms_helper.o drm_modeset_helper.o \
drm_scdc_helper.o drm_gem_framebuffer_helper.o \
drm_atomic_state_helper.o drm_damage_helper.o
drm_atomic_state_helper.o drm_damage_helper.o \
drm_format_helper.o
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
......
......@@ -2,7 +2,7 @@ config DRM_CIRRUS_QEMU
tristate "Cirrus driver for QEMU emulated device"
depends on DRM && PCI && MMU
select DRM_KMS_HELPER
select DRM_TTM
select DRM_GEM_SHMEM_HELPER
help
This is a KMS driver for emulated cirrus device in qemu.
It is *NOT* intended for real cirrus devices. This requires
......
cirrus-y := cirrus_main.o cirrus_mode.o \
cirrus_drv.o cirrus_fbdev.o cirrus_ttm.o
obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2012-2019 Red Hat
*
* This file is subject to the terms and conditions of the GNU General
* Public License version 2. See the file COPYING in the main
* directory of this archive for more details.
*
* Authors: Matthew Garrett
* Dave Airlie
* Gerd Hoffmann
*
* Portions of this code derived from cirrusfb.c:
* drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
*
* Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
*/
#include <linux/console.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <video/cirrus.h>
#include <video/vga.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_state_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_file.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
#define DRIVER_NAME "cirrus"
#define DRIVER_DESC "qemu cirrus vga"
#define DRIVER_DATE "2019"
#define DRIVER_MAJOR 2
#define DRIVER_MINOR 0
#define CIRRUS_MAX_PITCH (0x1FF << 3) /* (4096 - 1) & ~111b bytes */
#define CIRRUS_VRAM_SIZE (4 * 1024 * 1024) /* 4 MB */
struct cirrus_device {
struct drm_device dev;
struct drm_simple_display_pipe pipe;
struct drm_connector conn;
unsigned int cpp;
unsigned int pitch;
void __iomem *vram;
void __iomem *mmio;
};
/* ------------------------------------------------------------------ */
/*
* The meat of this driver. The core passes us a mode and we have to program
* it. The modesetting here is the bare minimum required to satisfy the qemu
* emulation of this hardware, and running this against a real device is
* likely to result in an inadequately programmed mode. We've already had
* the opportunity to modify the mode, so whatever we receive here should
* be something that can be correctly programmed and displayed
*/
#define SEQ_INDEX 4
#define SEQ_DATA 5
static u8 rreg_seq(struct cirrus_device *cirrus, u8 reg)
{
iowrite8(reg, cirrus->mmio + SEQ_INDEX);
return ioread8(cirrus->mmio + SEQ_DATA);
}
static void wreg_seq(struct cirrus_device *cirrus, u8 reg, u8 val)
{
iowrite8(reg, cirrus->mmio + SEQ_INDEX);
iowrite8(val, cirrus->mmio + SEQ_DATA);
}
#define CRT_INDEX 0x14
#define CRT_DATA 0x15
static u8 rreg_crt(struct cirrus_device *cirrus, u8 reg)
{
iowrite8(reg, cirrus->mmio + CRT_INDEX);
return ioread8(cirrus->mmio + CRT_DATA);
}
static void wreg_crt(struct cirrus_device *cirrus, u8 reg, u8 val)
{
iowrite8(reg, cirrus->mmio + CRT_INDEX);
iowrite8(val, cirrus->mmio + CRT_DATA);
}
#define GFX_INDEX 0xe
#define GFX_DATA 0xf
static void wreg_gfx(struct cirrus_device *cirrus, u8 reg, u8 val)
{
iowrite8(reg, cirrus->mmio + GFX_INDEX);
iowrite8(val, cirrus->mmio + GFX_DATA);
}
#define VGA_DAC_MASK 0x06
static void wreg_hdr(struct cirrus_device *cirrus, u8 val)
{
ioread8(cirrus->mmio + VGA_DAC_MASK);
ioread8(cirrus->mmio + VGA_DAC_MASK);
ioread8(cirrus->mmio + VGA_DAC_MASK);
ioread8(cirrus->mmio + VGA_DAC_MASK);
iowrite8(val, cirrus->mmio + VGA_DAC_MASK);
}
static int cirrus_convert_to(struct drm_framebuffer *fb)
{
if (fb->format->cpp[0] == 4 && fb->pitches[0] > CIRRUS_MAX_PITCH) {
if (fb->width * 3 <= CIRRUS_MAX_PITCH)
/* convert from XR24 to RG24 */
return 3;
else
/* convert from XR24 to RG16 */
return 2;
}
return 0;
}
static int cirrus_cpp(struct drm_framebuffer *fb)
{
int convert_cpp = cirrus_convert_to(fb);
if (convert_cpp)
return convert_cpp;
return fb->format->cpp[0];
}
static int cirrus_pitch(struct drm_framebuffer *fb)
{
int convert_cpp = cirrus_convert_to(fb);
if (convert_cpp)
return convert_cpp * fb->width;
return fb->pitches[0];
}
static void cirrus_set_start_address(struct cirrus_device *cirrus, u32 offset)
{
u32 addr;
u8 tmp;
addr = offset >> 2;
wreg_crt(cirrus, 0x0c, (u8)((addr >> 8) & 0xff));
wreg_crt(cirrus, 0x0d, (u8)(addr & 0xff));
tmp = rreg_crt(cirrus, 0x1b);
tmp &= 0xf2;
tmp |= (addr >> 16) & 0x01;
tmp |= (addr >> 15) & 0x0c;
wreg_crt(cirrus, 0x1b, tmp);
tmp = rreg_crt(cirrus, 0x1d);
tmp &= 0x7f;
tmp |= (addr >> 12) & 0x80;
wreg_crt(cirrus, 0x1d, tmp);
}
static int cirrus_mode_set(struct cirrus_device *cirrus,
struct drm_display_mode *mode,
struct drm_framebuffer *fb)
{
int hsyncstart, hsyncend, htotal, hdispend;
int vtotal, vdispend;
int tmp;
int sr07 = 0, hdr = 0;
htotal = mode->htotal / 8;
hsyncend = mode->hsync_end / 8;
hsyncstart = mode->hsync_start / 8;
hdispend = mode->hdisplay / 8;
vtotal = mode->vtotal;
vdispend = mode->vdisplay;
vdispend -= 1;
vtotal -= 2;
htotal -= 5;
hdispend -= 1;
hsyncstart += 1;
hsyncend += 1;
wreg_crt(cirrus, VGA_CRTC_V_SYNC_END, 0x20);
wreg_crt(cirrus, VGA_CRTC_H_TOTAL, htotal);
wreg_crt(cirrus, VGA_CRTC_H_DISP, hdispend);
wreg_crt(cirrus, VGA_CRTC_H_SYNC_START, hsyncstart);
wreg_crt(cirrus, VGA_CRTC_H_SYNC_END, hsyncend);
wreg_crt(cirrus, VGA_CRTC_V_TOTAL, vtotal & 0xff);
wreg_crt(cirrus, VGA_CRTC_V_DISP_END, vdispend & 0xff);
tmp = 0x40;
if ((vdispend + 1) & 512)
tmp |= 0x20;
wreg_crt(cirrus, VGA_CRTC_MAX_SCAN, tmp);
/*
* Overflow bits for values that don't fit in the standard registers
*/
tmp = 0x10;
if (vtotal & 0x100)
tmp |= 0x01;
if (vdispend & 0x100)
tmp |= 0x02;
if ((vdispend + 1) & 0x100)
tmp |= 0x08;
if (vtotal & 0x200)
tmp |= 0x20;
if (vdispend & 0x200)
tmp |= 0x40;
wreg_crt(cirrus, VGA_CRTC_OVERFLOW, tmp);
tmp = 0;
/* More overflow bits */
if ((htotal + 5) & 0x40)
tmp |= 0x10;
if ((htotal + 5) & 0x80)
tmp |= 0x20;
if (vtotal & 0x100)
tmp |= 0x40;
if (vtotal & 0x200)
tmp |= 0x80;
wreg_crt(cirrus, CL_CRT1A, tmp);
/* Disable Hercules/CGA compatibility */
wreg_crt(cirrus, VGA_CRTC_MODE, 0x03);
sr07 = rreg_seq(cirrus, 0x07);
sr07 &= 0xe0;
hdr = 0;
cirrus->cpp = cirrus_cpp(fb);
switch (cirrus->cpp * 8) {
case 8:
sr07 |= 0x11;
break;
case 16:
sr07 |= 0x17;
hdr = 0xc1;
break;
case 24:
sr07 |= 0x15;
hdr = 0xc5;
break;
case 32:
sr07 |= 0x19;
hdr = 0xc5;
break;
default:
return -1;
}
wreg_seq(cirrus, 0x7, sr07);
/* Program the pitch */
cirrus->pitch = cirrus_pitch(fb);
tmp = cirrus->pitch / 8;
wreg_crt(cirrus, VGA_CRTC_OFFSET, tmp);
/* Enable extended blanking and pitch bits, and enable full memory */
tmp = 0x22;
tmp |= (cirrus->pitch >> 7) & 0x10;
tmp |= (cirrus->pitch >> 6) & 0x40;
wreg_crt(cirrus, 0x1b, tmp);
/* Enable high-colour modes */
wreg_gfx(cirrus, VGA_GFX_MODE, 0x40);
/* And set graphics mode */
wreg_gfx(cirrus, VGA_GFX_MISC, 0x01);
wreg_hdr(cirrus, hdr);
cirrus_set_start_address(cirrus, 0);
/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
outb(0x20, 0x3c0);
return 0;
}
static int cirrus_fb_blit_rect(struct drm_framebuffer *fb,
struct drm_rect *rect)
{
struct cirrus_device *cirrus = fb->dev->dev_private;
void *vmap;
vmap = drm_gem_shmem_vmap(fb->obj[0]);
if (!vmap)
return -ENOMEM;
if (cirrus->cpp == fb->format->cpp[0])
drm_fb_memcpy_dstclip(__io_virt(cirrus->vram),
vmap, fb, rect);
else if (fb->format->cpp[0] == 4 && cirrus->cpp == 2)
drm_fb_xrgb8888_to_rgb565_dstclip(__io_virt(cirrus->vram),
cirrus->pitch,
vmap, fb, rect, false);
else if (fb->format->cpp[0] == 4 && cirrus->cpp == 3)
drm_fb_xrgb8888_to_rgb888_dstclip(__io_virt(cirrus->vram),
cirrus->pitch,
vmap, fb, rect);
else
WARN_ON_ONCE("cpp mismatch");
drm_gem_shmem_vunmap(fb->obj[0], vmap);
return 0;
}
static int cirrus_fb_blit_fullscreen(struct drm_framebuffer *fb)
{
struct drm_rect fullscreen = {
.x1 = 0,
.x2 = fb->width,
.y1 = 0,
.y2 = fb->height,
};
return cirrus_fb_blit_rect(fb, &fullscreen);
}
static int cirrus_check_size(int width, int height,
struct drm_framebuffer *fb)
{
int pitch = width * 2;
if (fb)
pitch = cirrus_pitch(fb);
if (pitch > CIRRUS_MAX_PITCH)
return -EINVAL;
if (pitch * height > CIRRUS_VRAM_SIZE)
return -EINVAL;
return 0;
}
/* ------------------------------------------------------------------ */
/* cirrus connector */
static int cirrus_conn_get_modes(struct drm_connector *conn)
{
int count;
count = drm_add_modes_noedid(conn,
conn->dev->mode_config.max_width,
conn->dev->mode_config.max_height);
drm_set_preferred_mode(conn, 1024, 768);
return count;
}
static const struct drm_connector_helper_funcs cirrus_conn_helper_funcs = {
.get_modes = cirrus_conn_get_modes,
};
static const struct drm_connector_funcs cirrus_conn_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static int cirrus_conn_init(struct cirrus_device *cirrus)
{
drm_connector_helper_add(&cirrus->conn, &cirrus_conn_helper_funcs);
return drm_connector_init(&cirrus->dev, &cirrus->conn,
&cirrus_conn_funcs, DRM_MODE_CONNECTOR_VGA);
}
/* ------------------------------------------------------------------ */
/* cirrus (simple) display pipe */
static enum drm_mode_status cirrus_pipe_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
if (cirrus_check_size(mode->hdisplay, mode->vdisplay, NULL) < 0)
return MODE_BAD;
return MODE_OK;
}
static int cirrus_pipe_check(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *plane_state,
struct drm_crtc_state *crtc_state)
{
struct drm_framebuffer *fb = plane_state->fb;
if (!fb)
return 0;
return cirrus_check_size(fb->width, fb->height, fb);
}
static void cirrus_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct cirrus_device *cirrus = pipe->crtc.dev->dev_private;
cirrus_mode_set(cirrus, &crtc_state->mode, plane_state->fb);
cirrus_fb_blit_fullscreen(plane_state->fb);
}
static void cirrus_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct cirrus_device *cirrus = pipe->crtc.dev->dev_private;
struct drm_plane_state *state = pipe->plane.state;
struct drm_crtc *crtc = &pipe->crtc;
struct drm_rect rect;
if (pipe->plane.state->fb &&
cirrus->cpp != cirrus_cpp(pipe->plane.state->fb))
cirrus_mode_set(cirrus, &crtc->mode,
pipe->plane.state->fb);
if (drm_atomic_helper_damage_merged(old_state, state, &rect))
cirrus_fb_blit_rect(pipe->plane.state->fb, &rect);
if (crtc->state->event) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
crtc->state->event = NULL;
spin_unlock_irq(&crtc->dev->event_lock);
}
}
static const struct drm_simple_display_pipe_funcs cirrus_pipe_funcs = {
.mode_valid = cirrus_pipe_mode_valid,
.check = cirrus_pipe_check,
.enable = cirrus_pipe_enable,
.update = cirrus_pipe_update,
};
static const uint32_t cirrus_formats[] = {
DRM_FORMAT_RGB565,
DRM_FORMAT_RGB888,
DRM_FORMAT_XRGB8888,
};
static const uint64_t cirrus_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
};
static int cirrus_pipe_init(struct cirrus_device *cirrus)
{
return drm_simple_display_pipe_init(&cirrus->dev,
&cirrus->pipe,
&cirrus_pipe_funcs,
cirrus_formats,
ARRAY_SIZE(cirrus_formats),
cirrus_modifiers,
&cirrus->conn);
}
/* ------------------------------------------------------------------ */
/* cirrus framebuffers & mode config */
static struct drm_framebuffer*
cirrus_fb_create(struct drm_device *dev, struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
if (mode_cmd->pixel_format != DRM_FORMAT_RGB565 &&
mode_cmd->pixel_format != DRM_FORMAT_RGB888 &&
mode_cmd->pixel_format != DRM_FORMAT_XRGB8888)
return ERR_PTR(-EINVAL);
if (cirrus_check_size(mode_cmd->width, mode_cmd->height, NULL) < 0)
return ERR_PTR(-EINVAL);
return drm_gem_fb_create_with_dirty(dev, file_priv, mode_cmd);
}
static const struct drm_mode_config_funcs cirrus_mode_config_funcs = {
.fb_create = cirrus_fb_create,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static void cirrus_mode_config_init(struct cirrus_device *cirrus)
{
struct drm_device *dev = &cirrus->dev;
drm_mode_config_init(dev);
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.max_width = CIRRUS_MAX_PITCH / 2;
dev->mode_config.max_height = 1024;
dev->mode_config.preferred_depth = 16;
dev->mode_config.prefer_shadow = 0;
dev->mode_config.funcs = &cirrus_mode_config_funcs;
}
/* ------------------------------------------------------------------ */
DEFINE_DRM_GEM_SHMEM_FOPS(cirrus_fops);
static struct drm_driver cirrus_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC | DRIVER_PRIME,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.fops = &cirrus_fops,
DRM_GEM_SHMEM_DRIVER_OPS,
};
static int cirrus_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct drm_device *dev;
struct cirrus_device *cirrus;
int ret;
ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "cirrusdrmfb");
if (ret)
return ret;
ret = pci_enable_device(pdev);
if (ret)
return ret;
ret = pci_request_regions(pdev, DRIVER_NAME);
if (ret)
return ret;
ret = -ENOMEM;
cirrus = kzalloc(sizeof(*cirrus), GFP_KERNEL);
if (cirrus == NULL)
goto err_pci_release;
dev = &cirrus->dev;
ret = drm_dev_init(dev, &cirrus_driver, &pdev->dev);
if (ret)
goto err_free_cirrus;
dev->dev_private = cirrus;
ret = -ENOMEM;
cirrus->vram = ioremap(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
if (cirrus->vram == NULL)
goto err_dev_put;
cirrus->mmio = ioremap(pci_resource_start(pdev, 1),
pci_resource_len(pdev, 1));
if (cirrus->mmio == NULL)
goto err_unmap_vram;
cirrus_mode_config_init(cirrus);
ret = cirrus_conn_init(cirrus);
if (ret < 0)
goto err_cleanup;
ret = cirrus_pipe_init(cirrus);
if (ret < 0)
goto err_cleanup;
drm_mode_config_reset(dev);
dev->pdev = pdev;
pci_set_drvdata(pdev, dev);
ret = drm_dev_register(dev, 0);
if (ret)
goto err_cleanup;
drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth);
return 0;
err_cleanup:
drm_mode_config_cleanup(dev);
iounmap(cirrus->mmio);
err_unmap_vram:
iounmap(cirrus->vram);
err_dev_put:
drm_dev_put(dev);
err_free_cirrus:
kfree(cirrus);
err_pci_release:
pci_release_regions(pdev);
return ret;
}
static void cirrus_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct cirrus_device *cirrus = dev->dev_private;
drm_dev_unregister(dev);
drm_mode_config_cleanup(dev);
iounmap(cirrus->mmio);
iounmap(cirrus->vram);
drm_dev_put(dev);
kfree(cirrus);
pci_release_regions(pdev);
}
static const struct pci_device_id pciidlist[] = {
{
.vendor = PCI_VENDOR_ID_CIRRUS,
.device = PCI_DEVICE_ID_CIRRUS_5446,
/* only bind to the cirrus chip in qemu */
.subvendor = PCI_SUBVENDOR_ID_REDHAT_QUMRANET,
.subdevice = PCI_SUBDEVICE_ID_QEMU,
}, {
.vendor = PCI_VENDOR_ID_CIRRUS,
.device = PCI_DEVICE_ID_CIRRUS_5446,
.subvendor = PCI_VENDOR_ID_XEN,
.subdevice = 0x0001,
},
{ /* end if list */ }
};
static struct pci_driver cirrus_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
.probe = cirrus_pci_probe,
.remove = cirrus_pci_remove,
};
static int __init cirrus_init(void)
{
if (vgacon_text_force())
return -EINVAL;
return pci_register_driver(&cirrus_pci_driver);
}
static void __exit cirrus_exit(void)
{
pci_unregister_driver(&cirrus_pci_driver);
}
module_init(cirrus_init);
module_exit(cirrus_exit);
MODULE_DEVICE_TABLE(pci, pciidlist);
MODULE_LICENSE("GPL");
/*
* Copyright 2012 Red Hat <mjg@redhat.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License version 2. See the file COPYING in the main
* directory of this archive for more details.
*
* Authors: Matthew Garrett
* Dave Airlie
*/
#include <linux/module.h>
#include <linux/console.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_probe_helper.h>
#include "cirrus_drv.h"
int cirrus_modeset = -1;
int cirrus_bpp = 16;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
module_param_named(modeset, cirrus_modeset, int, 0400);
MODULE_PARM_DESC(bpp, "Max bits-per-pixel (default:16)");
module_param_named(bpp, cirrus_bpp, int, 0400);
/*
* This is the generic driver code. This binds the driver to the drm core,
* which then performs further device association and calls our graphics init
* functions
*/
static struct drm_driver driver;
/* only bind to the cirrus chip in qemu */
static const struct pci_device_id pciidlist[] = {
{ PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
PCI_SUBVENDOR_ID_REDHAT_QUMRANET, PCI_SUBDEVICE_ID_QEMU,
0, 0, 0 },
{ PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, PCI_VENDOR_ID_XEN,
0x0001, 0, 0, 0 },
{0,}
};
static int cirrus_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int ret;
ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "cirrusdrmfb");
if (ret)
return ret;
return drm_get_pci_dev(pdev, ent, &driver);
}
static void cirrus_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_put_dev(dev);
}
#ifdef CONFIG_PM_SLEEP
static int cirrus_pm_suspend(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
struct cirrus_device *cdev = drm_dev->dev_private;
drm_kms_helper_poll_disable(drm_dev);
if (cdev->mode_info.gfbdev) {
console_lock();
drm_fb_helper_set_suspend(&cdev->mode_info.gfbdev->helper, 1);
console_unlock();
}
return 0;
}
static int cirrus_pm_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
struct cirrus_device *cdev = drm_dev->dev_private;
drm_helper_resume_force_mode(drm_dev);
if (cdev->mode_info.gfbdev) {
console_lock();
drm_fb_helper_set_suspend(&cdev->mode_info.gfbdev->helper, 0);
console_unlock();
}
drm_kms_helper_poll_enable(drm_dev);
return 0;
}
#endif
static const struct file_operations cirrus_driver_fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.mmap = cirrus_mmap,
.poll = drm_poll,
.compat_ioctl = drm_compat_ioctl,
};
static struct drm_driver driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM,
.load = cirrus_driver_load,
.unload = cirrus_driver_unload,
.fops = &cirrus_driver_fops,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
.gem_free_object_unlocked = cirrus_gem_free_object,
.dumb_create = cirrus_dumb_create,
.dumb_map_offset = cirrus_dumb_mmap_offset,
};
static const struct dev_pm_ops cirrus_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cirrus_pm_suspend,
cirrus_pm_resume)
};
static struct pci_driver cirrus_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
.probe = cirrus_pci_probe,
.remove = cirrus_pci_remove,
.driver.pm = &cirrus_pm_ops,
};
static int __init cirrus_init(void)
{
if (vgacon_text_force() && cirrus_modeset == -1)
return -EINVAL;
if (cirrus_modeset == 0)
return -EINVAL;
return pci_register_driver(&cirrus_pci_driver);
}
static void __exit cirrus_exit(void)
{
pci_unregister_driver(&cirrus_pci_driver);
}
module_init(cirrus_init);
module_exit(cirrus_exit);
MODULE_DEVICE_TABLE(pci, pciidlist);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
/*
* Copyright 2012 Red Hat
*
* This file is subject to the terms and conditions of the GNU General
* Public License version 2. See the file COPYING in the main
* directory of this archive for more details.
*
* Authors: Matthew Garrett
* Dave Airlie
*/
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_util.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
#include "cirrus_drv.h"
static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
int x, int y, int width, int height)
{
int i;
struct drm_gem_object *obj;
struct cirrus_bo *bo;
int src_offset, dst_offset;
int bpp = afbdev->gfb->format->cpp[0];
int ret = -EBUSY;
bool unmap = false;
bool store_for_later = false;
int x2, y2;
unsigned long flags;
obj = afbdev->gfb->obj[0];
bo = gem_to_cirrus_bo(obj);
/*
* try and reserve the BO, if we fail with busy
* then the BO is being moved and we should
* store up the damage until later.
*/
if (drm_can_sleep())
ret = cirrus_bo_reserve(bo, true);
if (ret) {
if (ret != -EBUSY)
return;
store_for_later = true;
}
x2 = x + width - 1;
y2 = y + height - 1;
spin_lock_irqsave(&afbdev->dirty_lock, flags);
if (afbdev->y1 < y)
y = afbdev->y1;
if (afbdev->y2 > y2)
y2 = afbdev->y2;
if (afbdev->x1 < x)
x = afbdev->x1;
if (afbdev->x2 > x2)
x2 = afbdev->x2;
if (store_for_later) {
afbdev->x1 = x;
afbdev->x2 = x2;
afbdev->y1 = y;
afbdev->y2 = y2;
spin_unlock_irqrestore(&afbdev->dirty_lock, flags);
return;
}
afbdev->x1 = afbdev->y1 = INT_MAX;
afbdev->x2 = afbdev->y2 = 0;
spin_unlock_irqrestore(&afbdev->dirty_lock, flags);
if (!bo->kmap.virtual) {
ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
if (ret) {
DRM_ERROR("failed to kmap fb updates\n");
cirrus_bo_unreserve(bo);
return;
}
unmap = true;
}
for (i = y; i < y + height; i++) {
/* assume equal stride for now */
src_offset = dst_offset = i * afbdev->gfb->pitches[0] + (x * bpp);
memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp);
}
if (unmap)
ttm_bo_kunmap(&bo->kmap);
cirrus_bo_unreserve(bo);
}
static void cirrus_fillrect(struct fb_info *info,
const struct fb_fillrect *rect)
{
struct cirrus_fbdev *afbdev = info->par;
drm_fb_helper_sys_fillrect(info, rect);
cirrus_dirty_update(afbdev, rect->dx, rect->dy, rect->width,
rect->height);
}
static void cirrus_copyarea(struct fb_info *info,
const struct fb_copyarea *area)
{
struct cirrus_fbdev *afbdev = info->par;
drm_fb_helper_sys_copyarea(info, area);
cirrus_dirty_update(afbdev, area->dx, area->dy, area->width,
area->height);
}
static void cirrus_imageblit(struct fb_info *info,
const struct fb_image *image)
{
struct cirrus_fbdev *afbdev = info->par;
drm_fb_helper_sys_imageblit(info, image);
cirrus_dirty_update(afbdev, image->dx, image->dy, image->width,
image->height);
}
static struct fb_ops cirrusfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
.fb_fillrect = cirrus_fillrect,
.fb_copyarea = cirrus_copyarea,
.fb_imageblit = cirrus_imageblit,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_blank = drm_fb_helper_blank,
.fb_setcmap = drm_fb_helper_setcmap,
};
static int cirrusfb_create_object(struct cirrus_fbdev *afbdev,
const struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object **gobj_p)
{
struct drm_device *dev = afbdev->helper.dev;
struct cirrus_device *cdev = dev->dev_private;
u32 bpp;
u32 size;
struct drm_gem_object *gobj;
int ret = 0;
bpp = drm_format_plane_cpp(mode_cmd->pixel_format, 0) * 8;
if (!cirrus_check_framebuffer(cdev, mode_cmd->width, mode_cmd->height,
bpp, mode_cmd->pitches[0]))
return -EINVAL;
size = mode_cmd->pitches[0] * mode_cmd->height;
ret = cirrus_gem_create(dev, size, true, &gobj);
if (ret)
return ret;
*gobj_p = gobj;
return ret;
}
static int cirrusfb_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct cirrus_fbdev *gfbdev =
container_of(helper, struct cirrus_fbdev, helper);
struct cirrus_device *cdev = gfbdev->helper.dev->dev_private;
struct fb_info *info;
struct drm_framebuffer *fb;
struct drm_mode_fb_cmd2 mode_cmd;
void *sysram;
struct drm_gem_object *gobj = NULL;
int size, ret;
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
size = mode_cmd.pitches[0] * mode_cmd.height;
ret = cirrusfb_create_object(gfbdev, &mode_cmd, &gobj);
if (ret) {
DRM_ERROR("failed to create fbcon backing object %d\n", ret);
return ret;
}
sysram = vmalloc(size);
if (!sysram)
return -ENOMEM;
info = drm_fb_helper_alloc_fbi(helper);
if (IS_ERR(info)) {
ret = PTR_ERR(info);
goto err_vfree;
}
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
if (!fb) {
ret = -ENOMEM;
goto err_drm_gem_object_put_unlocked;
}
ret = cirrus_framebuffer_init(cdev->dev, fb, &mode_cmd, gobj);
if (ret)
goto err_kfree;
gfbdev->sysram = sysram;
gfbdev->size = size;
gfbdev->gfb = fb;
/* setup helper */
gfbdev->helper.fb = fb;
info->fbops = &cirrusfb_ops;
drm_fb_helper_fill_info(info, &gfbdev->helper, sizes);
/* setup aperture base/size for vesafb takeover */
info->apertures->ranges[0].base = cdev->dev->mode_config.fb_base;
info->apertures->ranges[0].size = cdev->mc.vram_size;
info->fix.smem_start = cdev->dev->mode_config.fb_base;
info->fix.smem_len = cdev->mc.vram_size;
info->screen_base = sysram;
info->screen_size = size;
info->fix.mmio_start = 0;
info->fix.mmio_len = 0;
DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start);
DRM_INFO("vram aper at 0x%lX\n", (unsigned long)info->fix.smem_start);
DRM_INFO("size %lu\n", (unsigned long)info->fix.smem_len);
DRM_INFO("fb depth is %d\n", fb->format->depth);
DRM_INFO(" pitch is %d\n", fb->pitches[0]);
return 0;
err_kfree:
kfree(fb);
err_drm_gem_object_put_unlocked:
drm_gem_object_put_unlocked(gobj);
err_vfree:
vfree(sysram);
return ret;
}
static int cirrus_fbdev_destroy(struct drm_device *dev,
struct cirrus_fbdev *gfbdev)
{
struct drm_framebuffer *gfb = gfbdev->gfb;
drm_helper_force_disable_all(dev);
drm_fb_helper_unregister_fbi(&gfbdev->helper);
vfree(gfbdev->sysram);
drm_fb_helper_fini(&gfbdev->helper);
if (gfb)
drm_framebuffer_put(gfb);
return 0;
}
static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
.fb_probe = cirrusfb_create,
};
int cirrus_fbdev_init(struct cirrus_device *cdev)
{
struct cirrus_fbdev *gfbdev;
int ret;
/*bpp_sel = 8;*/
gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL);
if (!gfbdev)
return -ENOMEM;
cdev->mode_info.gfbdev = gfbdev;
spin_lock_init(&gfbdev->dirty_lock);
drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
&cirrus_fb_helper_funcs);
ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
CIRRUSFB_CONN_LIMIT);
if (ret)
return ret;
ret = drm_fb_helper_single_add_all_connectors(&gfbdev->helper);
if (ret)
return ret;
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(cdev->dev);
return drm_fb_helper_initial_config(&gfbdev->helper, cirrus_bpp);
}
void cirrus_fbdev_fini(struct cirrus_device *cdev)
{
if (!cdev->mode_info.gfbdev)
return;
cirrus_fbdev_destroy(cdev->dev, cdev->mode_info.gfbdev);
kfree(cdev->mode_info.gfbdev);
cdev->mode_info.gfbdev = NULL;
}
/*
* Copyright 2012 Red Hat
*
* This file is subject to the terms and conditions of the GNU General
* Public License version 2. See the file COPYING in the main
* directory of this archive for more details.
*
* Authors: Matthew Garrett
* Dave Airlie
*/
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include "cirrus_drv.h"
static const struct drm_framebuffer_funcs cirrus_fb_funcs = {
.create_handle = drm_gem_fb_create_handle,
.destroy = drm_gem_fb_destroy,
};
int cirrus_framebuffer_init(struct drm_device *dev,
struct drm_framebuffer *gfb,
const struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj)
{
int ret;
drm_helper_mode_fill_fb_struct(dev, gfb, mode_cmd);
gfb->obj[0] = obj;
ret = drm_framebuffer_init(dev, gfb, &cirrus_fb_funcs);
if (ret) {
DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
return ret;
}
return 0;
}
static struct drm_framebuffer *
cirrus_user_framebuffer_create(struct drm_device *dev,
struct drm_file *filp,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct cirrus_device *cdev = dev->dev_private;
struct drm_gem_object *obj;
struct drm_framebuffer *fb;
u32 bpp;
int ret;
bpp = drm_format_plane_cpp(mode_cmd->pixel_format, 0) * 8;
if (!cirrus_check_framebuffer(cdev, mode_cmd->width, mode_cmd->height,
bpp, mode_cmd->pitches[0]))
return ERR_PTR(-EINVAL);
obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
if (obj == NULL)
return ERR_PTR(-ENOENT);
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
if (!fb) {
drm_gem_object_put_unlocked(obj);
return ERR_PTR(-ENOMEM);
}
ret = cirrus_framebuffer_init(dev, fb, mode_cmd, obj);
if (ret) {
drm_gem_object_put_unlocked(obj);
kfree(fb);
return ERR_PTR(ret);
}
return fb;
}
static const struct drm_mode_config_funcs cirrus_mode_funcs = {
.fb_create = cirrus_user_framebuffer_create,
};
/* Unmap the framebuffer from the core and release the memory */
static void cirrus_vram_fini(struct cirrus_device *cdev)
{
iounmap(cdev->rmmio);
cdev->rmmio = NULL;
if (cdev->mc.vram_base)
release_mem_region(cdev->mc.vram_base, cdev->mc.vram_size);
}
/* Map the framebuffer from the card and configure the core */
static int cirrus_vram_init(struct cirrus_device *cdev)
{
/* BAR 0 is VRAM */
cdev->mc.vram_base = pci_resource_start(cdev->dev->pdev, 0);
cdev->mc.vram_size = pci_resource_len(cdev->dev->pdev, 0);
if (!request_mem_region(cdev->mc.vram_base, cdev->mc.vram_size,
"cirrusdrmfb_vram")) {
DRM_ERROR("can't reserve VRAM\n");
return -ENXIO;
}
return 0;
}
/*
* Our emulated hardware has two sets of memory. One is video RAM and can
* simply be used as a linear framebuffer - the other provides mmio access
* to the display registers. The latter can also be accessed via IO port
* access, but we map the range and use mmio to program them instead
*/
int cirrus_device_init(struct cirrus_device *cdev,
struct drm_device *ddev,
struct pci_dev *pdev, uint32_t flags)
{
int ret;
cdev->dev = ddev;
cdev->flags = flags;
/* Hardcode the number of CRTCs to 1 */
cdev->num_crtc = 1;
/* BAR 0 is the framebuffer, BAR 1 contains registers */
cdev->rmmio_base = pci_resource_start(cdev->dev->pdev, 1);
cdev->rmmio_size = pci_resource_len(cdev->dev->pdev, 1);
if (!request_mem_region(cdev->rmmio_base, cdev->rmmio_size,
"cirrusdrmfb_mmio")) {
DRM_ERROR("can't reserve mmio registers\n");
return -ENOMEM;
}
cdev->rmmio = ioremap(cdev->rmmio_base, cdev->rmmio_size);
if (cdev->rmmio == NULL)
return -ENOMEM;
ret = cirrus_vram_init(cdev);
if (ret) {
release_mem_region(cdev->rmmio_base, cdev->rmmio_size);
return ret;
}
return 0;
}
void cirrus_device_fini(struct cirrus_device *cdev)
{
release_mem_region(cdev->rmmio_base, cdev->rmmio_size);
cirrus_vram_fini(cdev);
}
/*
* Functions here will be called by the core once it's bound the driver to
* a PCI device
*/
int cirrus_driver_load(struct drm_device *dev, unsigned long flags)
{
struct cirrus_device *cdev;
int r;
cdev = kzalloc(sizeof(struct cirrus_device), GFP_KERNEL);
if (cdev == NULL)
return -ENOMEM;
dev->dev_private = (void *)cdev;
r = cirrus_device_init(cdev, dev, dev->pdev, flags);
if (r) {
dev_err(&dev->pdev->dev, "Fatal error during GPU init: %d\n", r);
goto out;
}
r = cirrus_mm_init(cdev);
if (r) {
dev_err(&dev->pdev->dev, "fatal err on mm init\n");
goto out;
}
/*
* cirrus_modeset_init() is initializing/registering the emulated fbdev
* and DRM internals can access/test some of the fields in
* mode_config->funcs as part of the fbdev registration process.
* Make sure dev->mode_config.funcs is properly set to avoid
* dereferencing a NULL pointer.
* FIXME: mode_config.funcs assignment should probably be done in
* cirrus_modeset_init() (that's a common pattern seen in other DRM
* drivers).
*/
dev->mode_config.funcs = &cirrus_mode_funcs;
r = cirrus_modeset_init(cdev);
if (r) {
dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r);
goto out;
}
return 0;
out:
cirrus_driver_unload(dev);
return r;
}
void cirrus_driver_unload(struct drm_device *dev)
{
struct cirrus_device *cdev = dev->dev_private;
if (cdev == NULL)
return;
cirrus_modeset_fini(cdev);
cirrus_mm_fini(cdev);
cirrus_device_fini(cdev);
kfree(cdev);
dev->dev_private = NULL;
}
int cirrus_gem_create(struct drm_device *dev,
u32 size, bool iskernel,
struct drm_gem_object **obj)
{
struct cirrus_bo *cirrusbo;
int ret;
*obj = NULL;
size = roundup(size, PAGE_SIZE);
if (size == 0)
return -EINVAL;
ret = cirrus_bo_create(dev, size, 0, 0, &cirrusbo);
if (ret) {
if (ret != -ERESTARTSYS)
DRM_ERROR("failed to allocate GEM object\n");
return ret;
}
*obj = &cirrusbo->gem;
return 0;
}
int cirrus_dumb_create(struct drm_file *file,
struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
int ret;
struct drm_gem_object *gobj;
u32 handle;
args->pitch = args->width * ((args->bpp + 7) / 8);
args->size = args->pitch * args->height;
ret = cirrus_gem_create(dev, args->size, false,
&gobj);
if (ret)
return ret;
ret = drm_gem_handle_create(file, gobj, &handle);
drm_gem_object_put_unlocked(gobj);
if (ret)
return ret;
args->handle = handle;
return 0;
}
static void cirrus_bo_unref(struct cirrus_bo **bo)
{
struct ttm_buffer_object *tbo;
if ((*bo) == NULL)
return;
tbo = &((*bo)->bo);
ttm_bo_put(tbo);
*bo = NULL;
}
void cirrus_gem_free_object(struct drm_gem_object *obj)
{
struct cirrus_bo *cirrus_bo = gem_to_cirrus_bo(obj);
cirrus_bo_unref(&cirrus_bo);
}
static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
{
return drm_vma_node_offset_addr(&bo->bo.vma_node);
}
int
cirrus_dumb_mmap_offset(struct drm_file *file,
struct drm_device *dev,
uint32_t handle,
uint64_t *offset)
{
struct drm_gem_object *obj;
struct cirrus_bo *bo;
obj = drm_gem_object_lookup(file, handle);
if (obj == NULL)
return -ENOENT;
bo = gem_to_cirrus_bo(obj);
*offset = cirrus_bo_mmap_offset(bo);
drm_gem_object_put_unlocked(obj);
return 0;
}
bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height,
int bpp, int pitch)
{
const int max_pitch = 0x1FF << 3; /* (4096 - 1) & ~111b bytes */
const int max_size = cdev->mc.vram_size;
if (bpp > cirrus_bpp)
return false;
if (bpp > 32)
return false;
if (pitch > max_pitch)
return false;
if (pitch * height > max_size)
return false;
return true;
}
/*
* Copyright 2012 Red Hat
*
* This file is subject to the terms and conditions of the GNU General
* Public License version 2. See the file COPYING in the main
* directory of this archive for more details.
*
* Authors: Matthew Garrett
* Dave Airlie
*
* Portions of this code derived from cirrusfb.c:
* drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
*
* Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
*/
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_probe_helper.h>
#include <video/cirrus.h>
#include "cirrus_drv.h"
#define CIRRUS_LUT_SIZE 256
#define PALETTE_INDEX 0x8
#define PALETTE_DATA 0x9
/*
* This file contains setup code for the CRTC.
*/
/*
* The DRM core requires DPMS functions, but they make little sense in our
* case and so are just stubs
*/
static void cirrus_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
struct cirrus_device *cdev = dev->dev_private;
u8 sr01, gr0e;
switch (mode) {
case DRM_MODE_DPMS_ON:
sr01 = 0x00;
gr0e = 0x00;
break;
case DRM_MODE_DPMS_STANDBY:
sr01 = 0x20;
gr0e = 0x02;
break;
case DRM_MODE_DPMS_SUSPEND:
sr01 = 0x20;
gr0e = 0x04;
break;
case DRM_MODE_DPMS_OFF:
sr01 = 0x20;
gr0e = 0x06;
break;
default:
return;
}
WREG8(SEQ_INDEX, 0x1);
sr01 |= RREG8(SEQ_DATA) & ~0x20;
WREG_SEQ(0x1, sr01);
WREG8(GFX_INDEX, 0xe);
gr0e |= RREG8(GFX_DATA) & ~0x06;
WREG_GFX(0xe, gr0e);
}
static void cirrus_set_start_address(struct drm_crtc *crtc, unsigned offset)
{
struct cirrus_device *cdev = crtc->dev->dev_private;
u32 addr;
u8 tmp;
addr = offset >> 2;
WREG_CRT(0x0c, (u8)((addr >> 8) & 0xff));
WREG_CRT(0x0d, (u8)(addr & 0xff));
WREG8(CRT_INDEX, 0x1b);
tmp = RREG8(CRT_DATA);
tmp &= 0xf2;
tmp |= (addr >> 16) & 0x01;
tmp |= (addr >> 15) & 0x0c;
WREG_CRT(0x1b, tmp);
WREG8(CRT_INDEX, 0x1d);
tmp = RREG8(CRT_DATA);
tmp &= 0x7f;
tmp |= (addr >> 12) & 0x80;
WREG_CRT(0x1d, tmp);
}
/* cirrus is different - we will force move buffers out of VRAM */
static int cirrus_crtc_do_set_base(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int x, int y, int atomic)
{
struct cirrus_device *cdev = crtc->dev->dev_private;
struct cirrus_bo *bo;
int ret;
u64 gpu_addr;
/* push the previous fb to system ram */
if (!atomic && fb) {
bo = gem_to_cirrus_bo(fb->obj[0]);
ret = cirrus_bo_reserve(bo, false);
if (ret)
return ret;
cirrus_bo_push_sysram(bo);
cirrus_bo_unreserve(bo);
}
bo = gem_to_cirrus_bo(crtc->primary->fb->obj[0]);
ret = cirrus_bo_reserve(bo, false);
if (ret)
return ret;
ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
if (ret) {
cirrus_bo_unreserve(bo);
return ret;
}
if (cdev->mode_info.gfbdev->gfb == crtc->primary->fb) {
/* if pushing console in kmap it */
ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
if (ret)
DRM_ERROR("failed to kmap fbcon\n");
}
cirrus_bo_unreserve(bo);
cirrus_set_start_address(crtc, (u32)gpu_addr);
return 0;
}
static int cirrus_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
return cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
}
/*
* The meat of this driver. The core passes us a mode and we have to program
* it. The modesetting here is the bare minimum required to satisfy the qemu
* emulation of this hardware, and running this against a real device is
* likely to result in an inadequately programmed mode. We've already had
* the opportunity to modify the mode, so whatever we receive here should
* be something that can be correctly programmed and displayed
*/
static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
int x, int y, struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct cirrus_device *cdev = dev->dev_private;
const struct drm_framebuffer *fb = crtc->primary->fb;
int hsyncstart, hsyncend, htotal, hdispend;
int vtotal, vdispend;
int tmp;
int sr07 = 0, hdr = 0;
htotal = mode->htotal / 8;
hsyncend = mode->hsync_end / 8;
hsyncstart = mode->hsync_start / 8;
hdispend = mode->hdisplay / 8;
vtotal = mode->vtotal;
vdispend = mode->vdisplay;
vdispend -= 1;
vtotal -= 2;
htotal -= 5;
hdispend -= 1;
hsyncstart += 1;
hsyncend += 1;
WREG_CRT(VGA_CRTC_V_SYNC_END, 0x20);
WREG_CRT(VGA_CRTC_H_TOTAL, htotal);
WREG_CRT(VGA_CRTC_H_DISP, hdispend);
WREG_CRT(VGA_CRTC_H_SYNC_START, hsyncstart);
WREG_CRT(VGA_CRTC_H_SYNC_END, hsyncend);
WREG_CRT(VGA_CRTC_V_TOTAL, vtotal & 0xff);
WREG_CRT(VGA_CRTC_V_DISP_END, vdispend & 0xff);
tmp = 0x40;
if ((vdispend + 1) & 512)
tmp |= 0x20;
WREG_CRT(VGA_CRTC_MAX_SCAN, tmp);
/*
* Overflow bits for values that don't fit in the standard registers
*/
tmp = 16;
if (vtotal & 256)
tmp |= 1;
if (vdispend & 256)
tmp |= 2;
if ((vdispend + 1) & 256)
tmp |= 8;
if (vtotal & 512)
tmp |= 32;
if (vdispend & 512)
tmp |= 64;
WREG_CRT(VGA_CRTC_OVERFLOW, tmp);
tmp = 0;
/* More overflow bits */
if ((htotal + 5) & 64)
tmp |= 16;
if ((htotal + 5) & 128)
tmp |= 32;
if (vtotal & 256)
tmp |= 64;
if (vtotal & 512)
tmp |= 128;
WREG_CRT(CL_CRT1A, tmp);
/* Disable Hercules/CGA compatibility */
WREG_CRT(VGA_CRTC_MODE, 0x03);
WREG8(SEQ_INDEX, 0x7);
sr07 = RREG8(SEQ_DATA);
sr07 &= 0xe0;
hdr = 0;
switch (fb->format->cpp[0] * 8) {
case 8:
sr07 |= 0x11;
break;
case 16:
sr07 |= 0x17;
hdr = 0xc1;
break;
case 24:
sr07 |= 0x15;
hdr = 0xc5;
break;
case 32:
sr07 |= 0x19;
hdr = 0xc5;
break;
default:
return -1;
}
WREG_SEQ(0x7, sr07);
/* Program the pitch */
tmp = fb->pitches[0] / 8;
WREG_CRT(VGA_CRTC_OFFSET, tmp);
/* Enable extended blanking and pitch bits, and enable full memory */
tmp = 0x22;
tmp |= (fb->pitches[0] >> 7) & 0x10;
tmp |= (fb->pitches[0] >> 6) & 0x40;
WREG_CRT(0x1b, tmp);
/* Enable high-colour modes */
WREG_GFX(VGA_GFX_MODE, 0x40);
/* And set graphics mode */
WREG_GFX(VGA_GFX_MISC, 0x01);
WREG_HDR(hdr);
cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
/* Unblank (needed on S3 resume, vgabios doesn't do it then) */
outb(0x20, 0x3c0);
return 0;
}
/*
* This is called before a mode is programmed. A typical use might be to
* enable DPMS during the programming to avoid seeing intermediate stages,
* but that's not relevant to us
*/
static void cirrus_crtc_prepare(struct drm_crtc *crtc)
{
}
static void cirrus_crtc_load_lut(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct cirrus_device *cdev = dev->dev_private;
u16 *r, *g, *b;
int i;
if (!crtc->enabled)
return;
r = crtc->gamma_store;
g = r + crtc->gamma_size;
b = g + crtc->gamma_size;
for (i = 0; i < CIRRUS_LUT_SIZE; i++) {
/* VGA registers */
WREG8(PALETTE_INDEX, i);
WREG8(PALETTE_DATA, *r++ >> 8);
WREG8(PALETTE_DATA, *g++ >> 8);
WREG8(PALETTE_DATA, *b++ >> 8);
}
}
/*
* This is called after a mode is programmed. It should reverse anything done
* by the prepare function
*/
static void cirrus_crtc_commit(struct drm_crtc *crtc)
{
cirrus_crtc_load_lut(crtc);
}
/*
* The core can pass us a set of gamma values to program. We actually only
* use this for 8-bit mode so can't perform smooth fades on deeper modes,
* but it's a requirement that we provide the function
*/
static int cirrus_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, uint32_t size,
struct drm_modeset_acquire_ctx *ctx)
{
cirrus_crtc_load_lut(crtc);
return 0;
}
/* Simple cleanup function */
static void cirrus_crtc_destroy(struct drm_crtc *crtc)
{
struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc);
drm_crtc_cleanup(crtc);
kfree(cirrus_crtc);
}
/* These provide the minimum set of functions required to handle a CRTC */
static const struct drm_crtc_funcs cirrus_crtc_funcs = {
.gamma_set = cirrus_crtc_gamma_set,
.set_config = drm_crtc_helper_set_config,
.destroy = cirrus_crtc_destroy,
};
static const struct drm_crtc_helper_funcs cirrus_helper_funcs = {
.dpms = cirrus_crtc_dpms,
.mode_set = cirrus_crtc_mode_set,
.mode_set_base = cirrus_crtc_mode_set_base,
.prepare = cirrus_crtc_prepare,
.commit = cirrus_crtc_commit,
};
/* CRTC setup */
static const uint32_t cirrus_formats_16[] = {
DRM_FORMAT_RGB565,
};
static const uint32_t cirrus_formats_24[] = {
DRM_FORMAT_RGB888,
DRM_FORMAT_RGB565,
};
static const uint32_t cirrus_formats_32[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_RGB565,
};
static struct drm_plane *cirrus_primary_plane(struct drm_device *dev)
{
const uint32_t *formats;
uint32_t nformats;
struct drm_plane *primary;
int ret;
switch (cirrus_bpp) {
case 16:
formats = cirrus_formats_16;
nformats = ARRAY_SIZE(cirrus_formats_16);
break;
case 24:
formats = cirrus_formats_24;
nformats = ARRAY_SIZE(cirrus_formats_24);
break;
case 32:
formats = cirrus_formats_32;
nformats = ARRAY_SIZE(cirrus_formats_32);
break;
default:
return NULL;
}
primary = kzalloc(sizeof(*primary), GFP_KERNEL);
if (primary == NULL) {
DRM_DEBUG_KMS("Failed to allocate primary plane\n");
return NULL;
}
ret = drm_universal_plane_init(dev, primary, 0,
&drm_primary_helper_funcs,
formats, nformats,
NULL,
DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret) {
kfree(primary);
primary = NULL;
}
return primary;
}
static void cirrus_crtc_init(struct drm_device *dev)
{
struct cirrus_device *cdev = dev->dev_private;
struct cirrus_crtc *cirrus_crtc;
struct drm_plane *primary;
cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) +
(CIRRUSFB_CONN_LIMIT * sizeof(struct drm_connector *)),
GFP_KERNEL);
if (cirrus_crtc == NULL)
return;
primary = cirrus_primary_plane(dev);
if (primary == NULL) {
kfree(cirrus_crtc);
return;
}
drm_crtc_init_with_planes(dev, &cirrus_crtc->base,
primary, NULL,
&cirrus_crtc_funcs, NULL);
drm_mode_crtc_set_gamma_size(&cirrus_crtc->base, CIRRUS_LUT_SIZE);
cdev->mode_info.crtc = cirrus_crtc;
drm_crtc_helper_add(&cirrus_crtc->base, &cirrus_helper_funcs);
}
static void cirrus_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
}
static void cirrus_encoder_dpms(struct drm_encoder *encoder, int state)
{
return;
}
static void cirrus_encoder_prepare(struct drm_encoder *encoder)
{
}
static void cirrus_encoder_commit(struct drm_encoder *encoder)
{
}
static void cirrus_encoder_destroy(struct drm_encoder *encoder)
{
struct cirrus_encoder *cirrus_encoder = to_cirrus_encoder(encoder);
drm_encoder_cleanup(encoder);
kfree(cirrus_encoder);
}
static const struct drm_encoder_helper_funcs cirrus_encoder_helper_funcs = {
.dpms = cirrus_encoder_dpms,
.mode_set = cirrus_encoder_mode_set,
.prepare = cirrus_encoder_prepare,
.commit = cirrus_encoder_commit,
};
static const struct drm_encoder_funcs cirrus_encoder_encoder_funcs = {
.destroy = cirrus_encoder_destroy,
};
static struct drm_encoder *cirrus_encoder_init(struct drm_device *dev)
{
struct drm_encoder *encoder;
struct cirrus_encoder *cirrus_encoder;
cirrus_encoder = kzalloc(sizeof(struct cirrus_encoder), GFP_KERNEL);
if (!cirrus_encoder)
return NULL;
encoder = &cirrus_encoder->base;
encoder->possible_crtcs = 0x1;
drm_encoder_init(dev, encoder, &cirrus_encoder_encoder_funcs,
DRM_MODE_ENCODER_DAC, NULL);
drm_encoder_helper_add(encoder, &cirrus_encoder_helper_funcs);
return encoder;
}
static int cirrus_vga_get_modes(struct drm_connector *connector)
{
int count;
/* Just add a static list of modes */
if (cirrus_bpp <= 24) {
count = drm_add_modes_noedid(connector, 1280, 1024);
drm_set_preferred_mode(connector, 1024, 768);
} else {
count = drm_add_modes_noedid(connector, 800, 600);
drm_set_preferred_mode(connector, 800, 600);
}
return count;
}
static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
*connector)
{
int enc_id = connector->encoder_ids[0];
/* pick the encoder ids */
if (enc_id)
return drm_encoder_find(connector->dev, NULL, enc_id);
return NULL;
}
static void cirrus_connector_destroy(struct drm_connector *connector)
{
drm_connector_cleanup(connector);
kfree(connector);
}
static const struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = {
.get_modes = cirrus_vga_get_modes,
.best_encoder = cirrus_connector_best_encoder,
};
static const struct drm_connector_funcs cirrus_vga_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = cirrus_connector_destroy,
};
static struct drm_connector *cirrus_vga_init(struct drm_device *dev)
{
struct drm_connector *connector;
struct cirrus_connector *cirrus_connector;
cirrus_connector = kzalloc(sizeof(struct cirrus_connector), GFP_KERNEL);
if (!cirrus_connector)
return NULL;
connector = &cirrus_connector->base;
drm_connector_init(dev, connector,
&cirrus_vga_connector_funcs, DRM_MODE_CONNECTOR_VGA);
drm_connector_helper_add(connector, &cirrus_vga_connector_helper_funcs);
drm_connector_register(connector);
return connector;
}
int cirrus_modeset_init(struct cirrus_device *cdev)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
int ret;
drm_mode_config_init(cdev->dev);
cdev->dev->mode_config.max_width = CIRRUS_MAX_FB_WIDTH;
cdev->dev->mode_config.max_height = CIRRUS_MAX_FB_HEIGHT;
cdev->dev->mode_config.fb_base = cdev->mc.vram_base;
cdev->dev->mode_config.preferred_depth = cirrus_bpp;
/* don't prefer a shadow on virt GPU */
cdev->dev->mode_config.prefer_shadow = 0;
cirrus_crtc_init(cdev->dev);
encoder = cirrus_encoder_init(cdev->dev);
if (!encoder) {
DRM_ERROR("cirrus_encoder_init failed\n");
return -1;
}
connector = cirrus_vga_init(cdev->dev);
if (!connector) {
DRM_ERROR("cirrus_vga_init failed\n");
return -1;
}
drm_connector_attach_encoder(connector, encoder);
ret = cirrus_fbdev_init(cdev);
if (ret) {
DRM_ERROR("cirrus_fbdev_init failed\n");
return ret;
}
return 0;
}
void cirrus_modeset_fini(struct cirrus_device *cdev)
{
cirrus_fbdev_fini(cdev);
drm_helper_force_disable_all(cdev->dev);
drm_mode_config_cleanup(cdev->dev);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 Noralf Trønnes
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_rect.h>
static void drm_fb_memcpy_lines(void *dst, unsigned int dst_pitch,
void *src, unsigned int src_pitch,
unsigned int linelength, unsigned int lines)
{
int line;
for (line = 0; line < lines; line++) {
memcpy(dst, src, linelength);
src += src_pitch;
dst += dst_pitch;
}
}
/**
* drm_fb_memcpy - Copy clip buffer
* @dst: Destination buffer
* @vaddr: Source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
*
* This function does not apply clipping on dst, i.e. the destination
* is a small buffer containing the clip rect only.
*/
void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip)
{
unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
unsigned int offset = (clip->y1 * fb->pitches[0]) + (clip->x1 * cpp);
size_t len = (clip->x2 - clip->x1) * cpp;
drm_fb_memcpy_lines(dst, len,
vaddr + offset, fb->pitches[0],
len, clip->y2 - clip->y1);
}
EXPORT_SYMBOL(drm_fb_memcpy);
/**
* drm_fb_memcpy_dstclip - Copy clip buffer
* @dst: Destination buffer
* @vaddr: Source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
*
* This function applies clipping on dst, i.e. the destination is a
* full framebuffer but only the clip rect content is copied over.
*/
void drm_fb_memcpy_dstclip(void *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip)
{
unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
unsigned int offset = (clip->y1 * fb->pitches[0]) + (clip->x1 * cpp);
size_t len = (clip->x2 - clip->x1) * cpp;
drm_fb_memcpy_lines(dst + offset, fb->pitches[0],
vaddr + offset, fb->pitches[0],
len, clip->y2 - clip->y1);
}
EXPORT_SYMBOL(drm_fb_memcpy_dstclip);
/**
* drm_fb_swab16 - Swap bytes into clip buffer
* @dst: RGB565 destination buffer
* @vaddr: RGB565 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
*/
void drm_fb_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip)
{
size_t len = (clip->x2 - clip->x1) * sizeof(u16);
unsigned int x, y;
u16 *src, *buf;
/*
* The cma memory is write-combined so reads are uncached.
* Speed up by fetching one line at a time.
*/
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return;
for (y = clip->y1; y < clip->y2; y++) {
src = vaddr + (y * fb->pitches[0]);
src += clip->x1;
memcpy(buf, src, len);
src = buf;
for (x = clip->x1; x < clip->x2; x++)
*dst++ = swab16(*src++);
}
kfree(buf);
}
EXPORT_SYMBOL(drm_fb_swab16);
static void drm_fb_xrgb8888_to_rgb565_lines(void *dst, unsigned int dst_pitch,
void *src, unsigned int src_pitch,
unsigned int src_linelength,
unsigned int lines,
bool swap)
{
unsigned int linepixels = src_linelength / sizeof(u32);
unsigned int x, y;
u32 *sbuf;
u16 *dbuf, val16;
/*
* The cma memory is write-combined so reads are uncached.
* Speed up by fetching one line at a time.
*/
sbuf = kmalloc(src_linelength, GFP_KERNEL);
if (!sbuf)
return;
for (y = 0; y < lines; y++) {
memcpy(sbuf, src, src_linelength);
dbuf = dst;
for (x = 0; x < linepixels; x++) {
val16 = ((sbuf[x] & 0x00F80000) >> 8) |
((sbuf[x] & 0x0000FC00) >> 5) |
((sbuf[x] & 0x000000F8) >> 3);
if (swap)
*dbuf++ = swab16(val16);
else
*dbuf++ = val16;
}
src += src_pitch;
dst += dst_pitch;
}
kfree(sbuf);
}
/**
* drm_fb_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
* @dst: RGB565 destination buffer
* @vaddr: XRGB8888 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
* @swap: Swap bytes
*
* Drivers can use this function for RGB565 devices that don't natively
* support XRGB8888.
*
* This function does not apply clipping on dst, i.e. the destination
* is a small buffer containing the clip rect only.
*/
void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr,
struct drm_framebuffer *fb,
struct drm_rect *clip, bool swap)
{
unsigned int src_offset = (clip->y1 * fb->pitches[0])
+ (clip->x1 * sizeof(u32));
size_t src_len = (clip->x2 - clip->x1) * sizeof(u32);
size_t dst_len = (clip->x2 - clip->x1) * sizeof(u16);
drm_fb_xrgb8888_to_rgb565_lines(dst, dst_len,
vaddr + src_offset, fb->pitches[0],
src_len, clip->y2 - clip->y1,
swap);
}
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565);
/**
* drm_fb_xrgb8888_to_rgb565_dstclip - Convert XRGB8888 to RGB565 clip buffer
* @dst: RGB565 destination buffer
* @dst_pitch: destination buffer pitch
* @vaddr: XRGB8888 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
* @swap: Swap bytes
*
* Drivers can use this function for RGB565 devices that don't natively
* support XRGB8888.
*
* This function applies clipping on dst, i.e. the destination is a
* full framebuffer but only the clip rect content is copied over.
*/
void drm_fb_xrgb8888_to_rgb565_dstclip(void *dst, unsigned int dst_pitch,
void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip, bool swap)
{
unsigned int src_offset = (clip->y1 * fb->pitches[0])
+ (clip->x1 * sizeof(u32));
unsigned int dst_offset = (clip->y1 * dst_pitch)
+ (clip->x1 * sizeof(u16));
size_t src_len = (clip->x2 - clip->x1) * sizeof(u32);
drm_fb_xrgb8888_to_rgb565_lines(dst + dst_offset, dst_pitch,
vaddr + src_offset, fb->pitches[0],
src_len, clip->y2 - clip->y1,
swap);
}
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_dstclip);
static void drm_fb_xrgb8888_to_rgb888_lines(void *dst, unsigned int dst_pitch,
void *src, unsigned int src_pitch,
unsigned int src_linelength,
unsigned int lines)
{
unsigned int linepixels = src_linelength / 3;
unsigned int x, y;
u32 *sbuf;
u8 *dbuf;
sbuf = kmalloc(src_linelength, GFP_KERNEL);
if (!sbuf)
return;
for (y = 0; y < lines; y++) {
memcpy(sbuf, src, src_linelength);
dbuf = dst;
for (x = 0; x < linepixels; x++) {
*dbuf++ = (sbuf[x] & 0x000000FF) >> 0;
*dbuf++ = (sbuf[x] & 0x0000FF00) >> 8;
*dbuf++ = (sbuf[x] & 0x00FF0000) >> 16;
}
src += src_pitch;
dst += dst_pitch;
}
kfree(sbuf);
}
/**
* drm_fb_xrgb8888_to_rgb888_dstclip - Convert XRGB8888 to RGB888 clip buffer
* @dst: RGB565 destination buffer
* @dst_pitch: destination buffer pitch
* @vaddr: XRGB8888 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
* @dstclip: Clip destination too.
*
* Drivers can use this function for RGB888 devices that don't natively
* support XRGB8888.
*
* This function applies clipping on dst, i.e. the destination is a
* full framebuffer but only the clip rect content is copied over.
*/
void drm_fb_xrgb8888_to_rgb888_dstclip(void *dst, unsigned int dst_pitch,
void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip)
{
unsigned int src_offset = (clip->y1 * fb->pitches[0])
+ (clip->x1 * sizeof(u32));
unsigned int dst_offset = (clip->y1 * dst_pitch)
+ (clip->x1 * 3);
size_t src_len = (clip->x2 - clip->x1) * sizeof(u32);
drm_fb_xrgb8888_to_rgb888_lines(dst + dst_offset, dst_pitch,
vaddr + src_offset, fb->pitches[0],
src_len, clip->y2 - clip->y1);
}
EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_dstclip);
/**
* drm_fb_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
* @dst: 8-bit grayscale destination buffer
* @vaddr: XRGB8888 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
*
* Drm doesn't have native monochrome or grayscale support.
* Such drivers can announce the commonly supported XR24 format to userspace
* and use this function to convert to the native format.
*
* Monochrome drivers will use the most significant bit,
* where 1 means foreground color and 0 background color.
*
* ITU BT.601 is used for the RGB -> luma (brightness) conversion.
*/
void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip)
{
unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
unsigned int x, y;
void *buf;
u32 *src;
if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
return;
/*
* The cma memory is write-combined so reads are uncached.
* Speed up by fetching one line at a time.
*/
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return;
for (y = clip->y1; y < clip->y2; y++) {
src = vaddr + (y * fb->pitches[0]);
src += clip->x1;
memcpy(buf, src, len);
src = buf;
for (x = clip->x1; x < clip->x2; x++) {
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 */
*dst++ = (3 * r + 6 * g + b) / 10;
src++;
}
}
kfree(buf);
}
EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8);
......@@ -5,6 +5,9 @@ config DRM_LIMA
tristate "LIMA (DRM support for ARM Mali 400/450 GPU)"
depends on DRM
depends on ARM || ARM64 || COMPILE_TEST
depends on MMU
depends on COMMON_CLK
depends on OF
select DRM_SCHED
help
DRM driver for ARM Mali 400/450 GPUs.
// SPDX-License-Identifier: GPL-2.0 OR MIT
/* Copyright 2017-2019 Qiang Yu <yuq825@gmail.com> */
#include <linux/mm.h>
#include <linux/sync_file.h>
#include <linux/pfn_t.h>
......
......@@ -39,12 +39,17 @@
#include "meson_viu.h"
#include "meson_registers.h"
#define MESON_G12A_VIU_OFFSET 0x5ec0
/* CRTC definition */
struct meson_crtc {
struct drm_crtc base;
struct drm_pending_vblank_event *event;
struct meson_drm *priv;
void (*enable_osd1)(struct meson_drm *priv);
void (*enable_vd1)(struct meson_drm *priv);
unsigned int viu_offset;
};
#define to_meson_crtc(x) container_of(x, struct meson_crtc, base)
......@@ -80,6 +85,44 @@ static const struct drm_crtc_funcs meson_crtc_funcs = {
};
static void meson_g12a_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
struct drm_crtc_state *crtc_state = crtc->state;
struct meson_drm *priv = meson_crtc->priv;
DRM_DEBUG_DRIVER("\n");
if (!crtc_state) {
DRM_ERROR("Invalid crtc_state\n");
return;
}
/* VD1 Preblend vertical start/end */
writel(FIELD_PREP(GENMASK(11, 0), 2303),
priv->io_base + _REG(VPP_PREBLEND_VD1_V_START_END));
/* Setup Blender */
writel(crtc_state->mode.hdisplay |
crtc_state->mode.vdisplay << 16,
priv->io_base + _REG(VPP_POSTBLEND_H_SIZE));
writel_relaxed(0 << 16 |
(crtc_state->mode.hdisplay - 1),
priv->io_base + _REG(VPP_OSD1_BLD_H_SCOPE));
writel_relaxed(0 << 16 |
(crtc_state->mode.vdisplay - 1),
priv->io_base + _REG(VPP_OSD1_BLD_V_SCOPE));
writel_relaxed(crtc_state->mode.hdisplay << 16 |
crtc_state->mode.vdisplay,
priv->io_base + _REG(VPP_OUT_H_V_SIZE));
drm_crtc_vblank_on(crtc);
priv->viu.osd1_enabled = true;
}
static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
......@@ -110,6 +153,31 @@ static void meson_crtc_atomic_enable(struct drm_crtc *crtc,
priv->viu.osd1_enabled = true;
}
static void meson_g12a_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
struct meson_drm *priv = meson_crtc->priv;
DRM_DEBUG_DRIVER("\n");
drm_crtc_vblank_off(crtc);
priv->viu.osd1_enabled = false;
priv->viu.osd1_commit = false;
priv->viu.vd1_enabled = false;
priv->viu.vd1_commit = false;
if (crtc->state->event && !crtc->state->active) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
static void meson_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
......@@ -173,6 +241,53 @@ static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = {
.atomic_disable = meson_crtc_atomic_disable,
};
static const struct drm_crtc_helper_funcs meson_g12a_crtc_helper_funcs = {
.atomic_begin = meson_crtc_atomic_begin,
.atomic_flush = meson_crtc_atomic_flush,
.atomic_enable = meson_g12a_crtc_atomic_enable,
.atomic_disable = meson_g12a_crtc_atomic_disable,
};
static void meson_crtc_enable_osd1(struct meson_drm *priv)
{
writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND,
priv->io_base + _REG(VPP_MISC));
}
static void meson_g12a_crtc_enable_osd1(struct meson_drm *priv)
{
writel_relaxed(priv->viu.osd_blend_din0_scope_h,
priv->io_base +
_REG(VIU_OSD_BLEND_DIN0_SCOPE_H));
writel_relaxed(priv->viu.osd_blend_din0_scope_v,
priv->io_base +
_REG(VIU_OSD_BLEND_DIN0_SCOPE_V));
writel_relaxed(priv->viu.osb_blend0_size,
priv->io_base +
_REG(VIU_OSD_BLEND_BLEND0_SIZE));
writel_relaxed(priv->viu.osb_blend1_size,
priv->io_base +
_REG(VIU_OSD_BLEND_BLEND1_SIZE));
}
static void meson_crtc_enable_vd1(struct meson_drm *priv)
{
writel_bits_relaxed(VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND |
VPP_COLOR_MNG_ENABLE,
VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND |
VPP_COLOR_MNG_ENABLE,
priv->io_base + _REG(VPP_MISC));
}
static void meson_g12a_crtc_enable_vd1(struct meson_drm *priv)
{
writel_relaxed(((1 << 16) | /* post bld premult*/
(1 << 8) | /* post src */
(1 << 4) | /* pre bld premult*/
(1 << 0)),
priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
}
void meson_crtc_irq(struct meson_drm *priv)
{
struct meson_crtc *meson_crtc = to_meson_crtc(priv->crtc);
......@@ -219,8 +334,8 @@ void meson_crtc_irq(struct meson_drm *priv)
MESON_CANVAS_BLKMODE_LINEAR, 0);
/* Enable OSD1 */
writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND,
priv->io_base + _REG(VPP_MISC));
if (meson_crtc->enable_osd1)
meson_crtc->enable_osd1(priv);
priv->viu.osd1_commit = false;
}
......@@ -261,89 +376,133 @@ void meson_crtc_irq(struct meson_drm *priv)
};
writel_relaxed(priv->viu.vd1_if0_gen_reg,
priv->io_base + _REG(VD1_IF0_GEN_REG));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_GEN_REG));
writel_relaxed(priv->viu.vd1_if0_gen_reg,
priv->io_base + _REG(VD2_IF0_GEN_REG));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_GEN_REG));
writel_relaxed(priv->viu.vd1_if0_gen_reg2,
priv->io_base + _REG(VD1_IF0_GEN_REG2));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_GEN_REG2));
writel_relaxed(priv->viu.viu_vd1_fmt_ctrl,
priv->io_base + _REG(VIU_VD1_FMT_CTRL));
priv->io_base + meson_crtc->viu_offset +
_REG(VIU_VD1_FMT_CTRL));
writel_relaxed(priv->viu.viu_vd1_fmt_ctrl,
priv->io_base + _REG(VIU_VD2_FMT_CTRL));
priv->io_base + meson_crtc->viu_offset +
_REG(VIU_VD2_FMT_CTRL));
writel_relaxed(priv->viu.viu_vd1_fmt_w,
priv->io_base + _REG(VIU_VD1_FMT_W));
priv->io_base + meson_crtc->viu_offset +
_REG(VIU_VD1_FMT_W));
writel_relaxed(priv->viu.viu_vd1_fmt_w,
priv->io_base + _REG(VIU_VD2_FMT_W));
priv->io_base + meson_crtc->viu_offset +
_REG(VIU_VD2_FMT_W));
writel_relaxed(priv->viu.vd1_if0_canvas0,
priv->io_base + _REG(VD1_IF0_CANVAS0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CANVAS0));
writel_relaxed(priv->viu.vd1_if0_canvas0,
priv->io_base + _REG(VD1_IF0_CANVAS1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CANVAS1));
writel_relaxed(priv->viu.vd1_if0_canvas0,
priv->io_base + _REG(VD2_IF0_CANVAS0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CANVAS0));
writel_relaxed(priv->viu.vd1_if0_canvas0,
priv->io_base + _REG(VD2_IF0_CANVAS1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CANVAS1));
writel_relaxed(priv->viu.vd1_if0_luma_x0,
priv->io_base + _REG(VD1_IF0_LUMA_X0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_LUMA_X0));
writel_relaxed(priv->viu.vd1_if0_luma_x0,
priv->io_base + _REG(VD1_IF0_LUMA_X1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_LUMA_X1));
writel_relaxed(priv->viu.vd1_if0_luma_x0,
priv->io_base + _REG(VD2_IF0_LUMA_X0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_LUMA_X0));
writel_relaxed(priv->viu.vd1_if0_luma_x0,
priv->io_base + _REG(VD2_IF0_LUMA_X1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_LUMA_X1));
writel_relaxed(priv->viu.vd1_if0_luma_y0,
priv->io_base + _REG(VD1_IF0_LUMA_Y0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_LUMA_Y0));
writel_relaxed(priv->viu.vd1_if0_luma_y0,
priv->io_base + _REG(VD1_IF0_LUMA_Y1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_LUMA_Y1));
writel_relaxed(priv->viu.vd1_if0_luma_y0,
priv->io_base + _REG(VD2_IF0_LUMA_Y0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_LUMA_Y0));
writel_relaxed(priv->viu.vd1_if0_luma_y0,
priv->io_base + _REG(VD2_IF0_LUMA_Y1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_LUMA_Y1));
writel_relaxed(priv->viu.vd1_if0_chroma_x0,
priv->io_base + _REG(VD1_IF0_CHROMA_X0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CHROMA_X0));
writel_relaxed(priv->viu.vd1_if0_chroma_x0,
priv->io_base + _REG(VD1_IF0_CHROMA_X1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CHROMA_X1));
writel_relaxed(priv->viu.vd1_if0_chroma_x0,
priv->io_base + _REG(VD2_IF0_CHROMA_X0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CHROMA_X0));
writel_relaxed(priv->viu.vd1_if0_chroma_x0,
priv->io_base + _REG(VD2_IF0_CHROMA_X1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CHROMA_X1));
writel_relaxed(priv->viu.vd1_if0_chroma_y0,
priv->io_base + _REG(VD1_IF0_CHROMA_Y0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CHROMA_Y0));
writel_relaxed(priv->viu.vd1_if0_chroma_y0,
priv->io_base + _REG(VD1_IF0_CHROMA_Y1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CHROMA_Y1));
writel_relaxed(priv->viu.vd1_if0_chroma_y0,
priv->io_base + _REG(VD2_IF0_CHROMA_Y0));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CHROMA_Y0));
writel_relaxed(priv->viu.vd1_if0_chroma_y0,
priv->io_base + _REG(VD2_IF0_CHROMA_Y1));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CHROMA_Y1));
writel_relaxed(priv->viu.vd1_if0_repeat_loop,
priv->io_base + _REG(VD1_IF0_RPT_LOOP));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_RPT_LOOP));
writel_relaxed(priv->viu.vd1_if0_repeat_loop,
priv->io_base + _REG(VD2_IF0_RPT_LOOP));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_RPT_LOOP));
writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat,
priv->io_base + _REG(VD1_IF0_LUMA0_RPT_PAT));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_LUMA0_RPT_PAT));
writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat,
priv->io_base + _REG(VD2_IF0_LUMA0_RPT_PAT));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_LUMA0_RPT_PAT));
writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat,
priv->io_base + _REG(VD1_IF0_LUMA1_RPT_PAT));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_LUMA1_RPT_PAT));
writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat,
priv->io_base + _REG(VD2_IF0_LUMA1_RPT_PAT));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_LUMA1_RPT_PAT));
writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat,
priv->io_base + _REG(VD1_IF0_CHROMA0_RPT_PAT));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CHROMA0_RPT_PAT));
writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat,
priv->io_base + _REG(VD2_IF0_CHROMA0_RPT_PAT));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CHROMA0_RPT_PAT));
writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat,
priv->io_base + _REG(VD1_IF0_CHROMA1_RPT_PAT));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CHROMA1_RPT_PAT));
writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat,
priv->io_base + _REG(VD2_IF0_CHROMA1_RPT_PAT));
writel_relaxed(0, priv->io_base + _REG(VD1_IF0_LUMA_PSEL));
writel_relaxed(0, priv->io_base + _REG(VD1_IF0_CHROMA_PSEL));
writel_relaxed(0, priv->io_base + _REG(VD2_IF0_LUMA_PSEL));
writel_relaxed(0, priv->io_base + _REG(VD2_IF0_CHROMA_PSEL));
priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CHROMA1_RPT_PAT));
writel_relaxed(0, priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_LUMA_PSEL));
writel_relaxed(0, priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_CHROMA_PSEL));
writel_relaxed(0, priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_LUMA_PSEL));
writel_relaxed(0, priv->io_base + meson_crtc->viu_offset +
_REG(VD2_IF0_CHROMA_PSEL));
writel_relaxed(priv->viu.vd1_range_map_y,
priv->io_base + _REG(VD1_IF0_RANGE_MAP_Y));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_RANGE_MAP_Y));
writel_relaxed(priv->viu.vd1_range_map_cb,
priv->io_base + _REG(VD1_IF0_RANGE_MAP_CB));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_RANGE_MAP_CB));
writel_relaxed(priv->viu.vd1_range_map_cr,
priv->io_base + _REG(VD1_IF0_RANGE_MAP_CR));
priv->io_base + meson_crtc->viu_offset +
_REG(VD1_IF0_RANGE_MAP_CR));
writel_relaxed(0x78404,
priv->io_base + _REG(VPP_SC_MISC));
writel_relaxed(priv->viu.vpp_pic_in_height,
......@@ -389,11 +548,8 @@ void meson_crtc_irq(struct meson_drm *priv)
writel_relaxed(0x42, priv->io_base + _REG(VPP_SCALE_COEF_IDX));
/* Enable VD1 */
writel_bits_relaxed(VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND |
VPP_COLOR_MNG_ENABLE,
VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND |
VPP_COLOR_MNG_ENABLE,
priv->io_base + _REG(VPP_MISC));
if (meson_crtc->enable_vd1)
meson_crtc->enable_vd1(priv);
priv->viu.vd1_commit = false;
}
......@@ -430,7 +586,16 @@ int meson_crtc_create(struct meson_drm *priv)
return ret;
}
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
meson_crtc->enable_osd1 = meson_g12a_crtc_enable_osd1;
meson_crtc->enable_vd1 = meson_g12a_crtc_enable_vd1;
meson_crtc->viu_offset = MESON_G12A_VIU_OFFSET;
drm_crtc_helper_add(crtc, &meson_g12a_crtc_helper_funcs);
} else {
meson_crtc->enable_osd1 = meson_crtc_enable_osd1;
meson_crtc->enable_vd1 = meson_crtc_enable_vd1;
drm_crtc_helper_add(crtc, &meson_crtc_helper_funcs);
}
priv->crtc = crtc;
......
......@@ -444,6 +444,7 @@ static const struct of_device_id dt_match[] = {
{ .compatible = "amlogic,meson-gxbb-vpu" },
{ .compatible = "amlogic,meson-gxl-vpu" },
{ .compatible = "amlogic,meson-gxm-vpu" },
{ .compatible = "amlogic,meson-g12a-vpu" },
{}
};
MODULE_DEVICE_TABLE(of, dt_match);
......
......@@ -62,6 +62,10 @@ struct meson_drm {
uint32_t osd_sc_h_phase_step;
uint32_t osd_sc_h_ctrl0;
uint32_t osd_sc_v_ctrl0;
uint32_t osd_blend_din0_scope_h;
uint32_t osd_blend_din0_scope_v;
uint32_t osb_blend0_size;
uint32_t osb_blend1_size;
bool vd1_enabled;
bool vd1_commit;
......
......@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/component.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/reset.h>
#include <linux/clk.h>
......@@ -105,6 +106,7 @@
#define HDMITX_TOP_ADDR_REG 0x0
#define HDMITX_TOP_DATA_REG 0x4
#define HDMITX_TOP_CTRL_REG 0x8
#define HDMITX_TOP_G12A_OFFSET 0x8000
/* Controller Communication Channel */
#define HDMITX_DWC_ADDR_REG 0x10
......@@ -118,6 +120,8 @@
#define HHI_HDMI_PHY_CNTL1 0x3a4 /* 0xe9 */
#define HHI_HDMI_PHY_CNTL2 0x3a8 /* 0xea */
#define HHI_HDMI_PHY_CNTL3 0x3ac /* 0xeb */
#define HHI_HDMI_PHY_CNTL4 0x3b0 /* 0xec */
#define HHI_HDMI_PHY_CNTL5 0x3b4 /* 0xed */
static DEFINE_SPINLOCK(reg_lock);
......@@ -127,12 +131,26 @@ enum meson_venc_source {
MESON_VENC_SOURCE_ENCP = 2,
};
struct meson_dw_hdmi;
struct meson_dw_hdmi_data {
unsigned int (*top_read)(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr);
void (*top_write)(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr, unsigned int data);
unsigned int (*dwc_read)(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr);
void (*dwc_write)(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr, unsigned int data);
};
struct meson_dw_hdmi {
struct drm_encoder encoder;
struct dw_hdmi_plat_data dw_plat_data;
struct meson_drm *priv;
struct device *dev;
void __iomem *hdmitx;
const struct meson_dw_hdmi_data *data;
struct reset_control *hdmitx_apb;
struct reset_control *hdmitx_ctrl;
struct reset_control *hdmitx_phy;
......@@ -174,6 +192,12 @@ static unsigned int dw_hdmi_top_read(struct meson_dw_hdmi *dw_hdmi,
return data;
}
static unsigned int dw_hdmi_g12a_top_read(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr)
{
return readl(dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2));
}
static inline void dw_hdmi_top_write(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr, unsigned int data)
{
......@@ -191,18 +215,24 @@ static inline void dw_hdmi_top_write(struct meson_dw_hdmi *dw_hdmi,
spin_unlock_irqrestore(&reg_lock, flags);
}
static inline void dw_hdmi_g12a_top_write(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr, unsigned int data)
{
writel(data, dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2));
}
/* Helper to change specific bits in PHY registers */
static inline void dw_hdmi_top_write_bits(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr,
unsigned int mask,
unsigned int val)
{
unsigned int data = dw_hdmi_top_read(dw_hdmi, addr);
unsigned int data = dw_hdmi->data->top_read(dw_hdmi, addr);
data &= ~mask;
data |= val;
dw_hdmi_top_write(dw_hdmi, addr, data);
dw_hdmi->data->top_write(dw_hdmi, addr, data);
}
static unsigned int dw_hdmi_dwc_read(struct meson_dw_hdmi *dw_hdmi,
......@@ -226,6 +256,12 @@ static unsigned int dw_hdmi_dwc_read(struct meson_dw_hdmi *dw_hdmi,
return data;
}
static unsigned int dw_hdmi_g12a_dwc_read(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr)
{
return readb(dw_hdmi->hdmitx + addr);
}
static inline void dw_hdmi_dwc_write(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr, unsigned int data)
{
......@@ -243,18 +279,24 @@ static inline void dw_hdmi_dwc_write(struct meson_dw_hdmi *dw_hdmi,
spin_unlock_irqrestore(&reg_lock, flags);
}
static inline void dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr, unsigned int data)
{
writeb(data, dw_hdmi->hdmitx + addr);
}
/* Helper to change specific bits in controller registers */
static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi,
unsigned int addr,
unsigned int mask,
unsigned int val)
{
unsigned int data = dw_hdmi_dwc_read(dw_hdmi, addr);
unsigned int data = dw_hdmi->data->dwc_read(dw_hdmi, addr);
data &= ~mask;
data |= val;
dw_hdmi_dwc_write(dw_hdmi, addr, data);
dw_hdmi->data->dwc_write(dw_hdmi, addr, data);
}
/* Bridge */
......@@ -300,6 +342,24 @@ static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi,
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33632122);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2000115b);
}
} else if (dw_hdmi_is_compatible(dw_hdmi,
"amlogic,meson-g12a-dw-hdmi")) {
if (pixel_clock >= 371250) {
/* 5.94Gbps, 3.7125Gbps */
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x37eb65c4);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x0000080b);
} else if (pixel_clock >= 297000) {
/* 2.97Gbps */
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb6262);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003);
} else {
/* 1.485Gbps, and below */
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb4242);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003);
}
}
}
......@@ -375,7 +435,7 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0);
/* Bring out of reset */
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_SW_RESET, 0);
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_SW_RESET, 0);
/* Enable internal pixclk, tmds_clk, spdif_clk, i2s_clk, cecclk */
dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL,
......@@ -384,24 +444,25 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
0x3 << 4, 0x3 << 4);
/* Enable normal output to PHY */
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
/* TMDS pattern setup (TOFIX Handle the YUV420 case) */
if (mode->clock > 340000) {
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, 0);
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
0);
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
0x03ff03ff);
} else {
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
0x001f001f);
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
0x001f001f);
}
/* Load TMDS pattern */
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1);
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1);
msleep(20);
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2);
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2);
/* Setup PHY parameters */
meson_hdmi_phy_setup_mode(dw_hdmi, mode);
......@@ -412,7 +473,8 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
/* BIT_INVERT */
if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi"))
dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
BIT(17), 0);
else
......@@ -480,7 +542,7 @@ static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi,
{
struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
return !!dw_hdmi_top_read(dw_hdmi, HDMITX_TOP_STAT0) ?
return !!dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_STAT0) ?
connector_status_connected : connector_status_disconnected;
}
......@@ -490,11 +552,11 @@ static void dw_hdmi_setup_hpd(struct dw_hdmi *hdmi,
struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
/* Setup HPD Filter */
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER,
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER,
(0xa << 12) | 0xa0);
/* Clear interrupts */
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL);
/* Unmask interrupts */
......@@ -515,8 +577,8 @@ static irqreturn_t dw_hdmi_top_irq(int irq, void *dev_id)
struct meson_dw_hdmi *dw_hdmi = dev_id;
u32 stat;
stat = dw_hdmi_top_read(dw_hdmi, HDMITX_TOP_INTR_STAT);
dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, stat);
stat = dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_INTR_STAT);
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, stat);
/* HPD Events, handle in the threaded interrupt handler */
if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) {
......@@ -685,7 +747,9 @@ static const struct drm_encoder_helper_funcs
static int meson_dw_hdmi_reg_read(void *context, unsigned int reg,
unsigned int *result)
{
*result = dw_hdmi_dwc_read(context, reg);
struct meson_dw_hdmi *dw_hdmi = context;
*result = dw_hdmi->data->dwc_read(dw_hdmi, reg);
return 0;
......@@ -694,7 +758,9 @@ static int meson_dw_hdmi_reg_read(void *context, unsigned int reg,
static int meson_dw_hdmi_reg_write(void *context, unsigned int reg,
unsigned int val)
{
dw_hdmi_dwc_write(context, reg, val);
struct meson_dw_hdmi *dw_hdmi = context;
dw_hdmi->data->dwc_write(dw_hdmi, reg, val);
return 0;
}
......@@ -708,6 +774,20 @@ static const struct regmap_config meson_dw_hdmi_regmap_config = {
.fast_io = true,
};
static const struct meson_dw_hdmi_data meson_dw_hdmi_gx_data = {
.top_read = dw_hdmi_top_read,
.top_write = dw_hdmi_top_write,
.dwc_read = dw_hdmi_dwc_read,
.dwc_write = dw_hdmi_dwc_write,
};
static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = {
.top_read = dw_hdmi_g12a_top_read,
.top_write = dw_hdmi_g12a_top_write,
.dwc_read = dw_hdmi_g12a_dwc_read,
.dwc_write = dw_hdmi_g12a_dwc_write,
};
static bool meson_hdmi_connector_is_available(struct device *dev)
{
struct device_node *ep, *remote;
......@@ -734,6 +814,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
void *data)
{
struct platform_device *pdev = to_platform_device(dev);
const struct meson_dw_hdmi_data *match;
struct meson_dw_hdmi *meson_dw_hdmi;
struct drm_device *drm = data;
struct meson_drm *priv = drm->dev_private;
......@@ -750,6 +831,12 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
return -ENODEV;
}
match = of_device_get_match_data(&pdev->dev);
if (!match) {
dev_err(&pdev->dev, "failed to get match data\n");
return -ENODEV;
}
meson_dw_hdmi = devm_kzalloc(dev, sizeof(*meson_dw_hdmi),
GFP_KERNEL);
if (!meson_dw_hdmi)
......@@ -757,6 +844,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
meson_dw_hdmi->priv = priv;
meson_dw_hdmi->dev = dev;
meson_dw_hdmi->data = match;
dw_plat_data = &meson_dw_hdmi->dw_plat_data;
encoder = &meson_dw_hdmi->encoder;
......@@ -857,23 +945,27 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
reset_control_reset(meson_dw_hdmi->hdmitx_phy);
/* Enable APB3 fail on error */
if (!meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
writel_bits_relaxed(BIT(15), BIT(15),
meson_dw_hdmi->hdmitx + HDMITX_TOP_CTRL_REG);
writel_bits_relaxed(BIT(15), BIT(15),
meson_dw_hdmi->hdmitx + HDMITX_DWC_CTRL_REG);
}
/* Bring out of reset */
dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_SW_RESET, 0);
meson_dw_hdmi->data->top_write(meson_dw_hdmi,
HDMITX_TOP_SW_RESET, 0);
msleep(20);
dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_CLK_CNTL, 0xff);
meson_dw_hdmi->data->top_write(meson_dw_hdmi,
HDMITX_TOP_CLK_CNTL, 0xff);
/* Enable HDMI-TX Interrupt */
dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
HDMITX_TOP_INTR_CORE);
dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN,
meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN,
HDMITX_TOP_INTR_CORE);
/* Bridge / Connector */
......@@ -923,9 +1015,14 @@ static int meson_dw_hdmi_remove(struct platform_device *pdev)
}
static const struct of_device_id meson_dw_hdmi_of_table[] = {
{ .compatible = "amlogic,meson-gxbb-dw-hdmi" },
{ .compatible = "amlogic,meson-gxl-dw-hdmi" },
{ .compatible = "amlogic,meson-gxm-dw-hdmi" },
{ .compatible = "amlogic,meson-gxbb-dw-hdmi",
.data = &meson_dw_hdmi_gx_data },
{ .compatible = "amlogic,meson-gxl-dw-hdmi",
.data = &meson_dw_hdmi_gx_data },
{ .compatible = "amlogic,meson-gxm-dw-hdmi",
.data = &meson_dw_hdmi_gx_data },
{ .compatible = "amlogic,meson-g12a-dw-hdmi",
.data = &meson_dw_hdmi_g12a_data },
{ }
};
MODULE_DEVICE_TABLE(of, meson_dw_hdmi_of_table);
......
......@@ -21,9 +21,12 @@
#define __MESON_DW_HDMI_H
/*
* Bit 7 RW Reserved. Default 1.
* Bit 6 RW Reserved. Default 1.
* Bit 5 RW Reserved. Default 1.
* Bit 15-10: RW Reserved. Default 1 starting from G12A
* Bit 9 RW sw_reset_i2c starting from G12A
* Bit 8 RW sw_reset_axiarb starting from G12A
* Bit 7 RW Reserved. Default 1, sw_reset_emp starting from G12A
* Bit 6 RW Reserved. Default 1, sw_reset_flt starting from G12A
* Bit 5 RW Reserved. Default 1, sw_reset_hdcp22 starting from G12A
* Bit 4 RW sw_reset_phyif: PHY interface. 1=Apply reset; 0=Release from reset.
* Default 1.
* Bit 3 RW sw_reset_intr: interrupt module. 1=Apply reset;
......@@ -39,12 +42,16 @@
#define HDMITX_TOP_SW_RESET (0x000)
/*
* Bit 31 RW free_clk_en: 0=Enable clock gating for power saving; 1= Disable
* Bit 12 RW i2s_ws_inv:1=Invert i2s_ws; 0=No invert. Default 0.
* Bit 11 RW i2s_clk_inv: 1=Invert i2s_clk; 0=No invert. Default 0.
* Bit 10 RW spdif_clk_inv: 1=Invert spdif_clk; 0=No invert. Default 0.
* Bit 9 RW tmds_clk_inv: 1=Invert tmds_clk; 0=No invert. Default 0.
* Bit 8 RW pixel_clk_inv: 1=Invert pixel_clk; 0=No invert. Default 0.
* Bit 4 RW cec_clk_en: 1=enable cec_clk; 0=disable. Default 0.
* Bit 7 RW hdcp22_skpclk_en: starting from G12A, 1=enable; 0=disable
* Bit 6 RW hdcp22_esmclk_en: starting from G12A, 1=enable; 0=disable
* Bit 5 RW hdcp22_tmdsclk_en: starting from G12A, 1=enable; 0=disable
* Bit 4 RW cec_clk_en: 1=enable cec_clk; 0=disable. Default 0. Reserved for G12A
* Bit 3 RW i2s_clk_en: 1=enable i2s_clk; 0=disable. Default 0.
* Bit 2 RW spdif_clk_en: 1=enable spdif_clk; 0=disable. Default 0.
* Bit 1 RW tmds_clk_en: 1=enable tmds_clk; 0=disable. Default 0.
......@@ -53,6 +60,8 @@
#define HDMITX_TOP_CLK_CNTL (0x001)
/*
* Bit 31:28 RW rxsense_glitch_width: starting from G12A
* Bit 27:16 RW rxsense_valid_width: starting from G12A
* Bit 11: 0 RW hpd_valid_width: filter out width <= M*1024. Default 0.
* Bit 15:12 RW hpd_glitch_width: filter out glitch <= N. Default 0.
*/
......@@ -61,6 +70,9 @@
/*
* intr_maskn: MASK_N, one bit per interrupt source.
* 1=Enable interrupt source; 0=Disable interrupt source. Default 0.
* [ 7] rxsense_fall starting from G12A
* [ 6] rxsense_rise starting from G12A
* [ 5] err_i2c_timeout starting from G12A
* [ 4] hdcp22_rndnum_err
* [ 3] nonce_rfrsh_rise
* [ 2] hpd_fall_intr
......@@ -73,6 +85,9 @@
* Bit 30: 0 RW intr_stat: For each bit, write 1 to manually set the interrupt
* bit, read back the interrupt status.
* Bit 31 R IP interrupt status
* Bit 7 RW rxsense_fall starting from G12A
* Bit 6 RW rxsense_rise starting from G12A
* Bit 5 RW err_i2c_timeout starting from G12A
* Bit 2 RW hpd_fall
* Bit 1 RW hpd_rise
* Bit 0 RW IP interrupt
......@@ -80,6 +95,9 @@
#define HDMITX_TOP_INTR_STAT (0x004)
/*
* [7] rxsense_fall starting from G12A
* [6] rxsense_rise starting from G12A
* [5] err_i2c_timeout starting from G12A
* [4] hdcp22_rndnum_err
* [3] nonce_rfrsh_rise
* [2] hpd_fall
......@@ -91,6 +109,8 @@
#define HDMITX_TOP_INTR_CORE BIT(0)
#define HDMITX_TOP_INTR_HPD_RISE BIT(1)
#define HDMITX_TOP_INTR_HPD_FALL BIT(2)
#define HDMITX_TOP_INTR_RXSENSE_RISE BIT(6)
#define HDMITX_TOP_INTR_RXSENSE_FALL BIT(7)
/* Bit 14:12 RW tmds_sel: 3'b000=Output zero; 3'b001=Output normal TMDS data;
* 3'b010=Output PRBS data; 3'b100=Output shift pattern. Default 0.
......@@ -140,7 +160,9 @@
*/
#define HDMITX_TOP_REVOCMEM_STAT (0x00D)
/* Bit 0 R filtered HPD status. */
/* Bit 1 R filtered RxSense status
* Bit 0 R filtered HPD status.
*/
#define HDMITX_TOP_STAT0 (0x00E)
#endif /* __MESON_DW_HDMI_H */
......@@ -516,6 +516,12 @@ static void meson_overlay_atomic_disable(struct drm_plane *plane,
priv->viu.vd1_enabled = false;
/* Disable VD1 */
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
writel_relaxed(0, priv->io_base + _REG(VD1_IF0_GEN_REG + 0x17b0));
writel_relaxed(0, priv->io_base + _REG(VD2_IF0_GEN_REG + 0x17b0));
} else
writel_bits_relaxed(VPP_VD1_POSTBLEND | VPP_VD1_PREBLEND, 0,
priv->io_base + _REG(VPP_MISC));
......
......@@ -294,6 +294,13 @@ static void meson_plane_atomic_update(struct drm_plane *plane,
priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1;
priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1;
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
priv->viu.osd_blend_din0_scope_h = ((dest.x2 - 1) << 16) | dest.x1;
priv->viu.osd_blend_din0_scope_v = ((dest.y2 - 1) << 16) | dest.y1;
priv->viu.osb_blend0_size = dst_h << 16 | dst_w;
priv->viu.osb_blend1_size = dst_h << 16 | dst_w;
}
/* Update Canvas with buffer address */
gem = drm_fb_cma_get_gem_obj(fb, 0);
......@@ -320,6 +327,10 @@ static void meson_plane_atomic_disable(struct drm_plane *plane,
struct meson_drm *priv = meson_plane->priv;
/* Disable OSD1 */
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
writel_bits_relaxed(BIT(0) | BIT(21), 0,
priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
else
writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0,
priv->io_base + _REG(VPP_MISC));
......
......@@ -216,6 +216,29 @@
#define VIU_OSD2_FIFO_CTRL_STAT 0x1a4b
#define VIU_OSD2_TEST_RDDATA 0x1a4c
#define VIU_OSD2_PROT_CTRL 0x1a4e
#define VIU_OSD2_MALI_UNPACK_CTRL 0x1abd
#define VIU_OSD2_DIMM_CTRL 0x1acf
#define VIU_OSD3_CTRL_STAT 0x3d80
#define VIU_OSD3_CTRL_STAT2 0x3d81
#define VIU_OSD3_COLOR_ADDR 0x3d82
#define VIU_OSD3_COLOR 0x3d83
#define VIU_OSD3_TCOLOR_AG0 0x3d84
#define VIU_OSD3_TCOLOR_AG1 0x3d85
#define VIU_OSD3_TCOLOR_AG2 0x3d86
#define VIU_OSD3_TCOLOR_AG3 0x3d87
#define VIU_OSD3_BLK0_CFG_W0 0x3d88
#define VIU_OSD3_BLK0_CFG_W1 0x3d8c
#define VIU_OSD3_BLK0_CFG_W2 0x3d90
#define VIU_OSD3_BLK0_CFG_W3 0x3d94
#define VIU_OSD3_BLK0_CFG_W4 0x3d98
#define VIU_OSD3_BLK1_CFG_W4 0x3d99
#define VIU_OSD3_BLK2_CFG_W4 0x3d9a
#define VIU_OSD3_FIFO_CTRL_STAT 0x3d9c
#define VIU_OSD3_TEST_RDDATA 0x3d9d
#define VIU_OSD3_PROT_CTRL 0x3d9e
#define VIU_OSD3_MALI_UNPACK_CTRL 0x3d9f
#define VIU_OSD3_DIMM_CTRL 0x3da0
#define VD1_IF0_GEN_REG 0x1a50
#define VD1_IF0_CANVAS0 0x1a51
......@@ -287,6 +310,27 @@
#define VIU_OSD1_MATRIX_COEF31_32 0x1a9e
#define VIU_OSD1_MATRIX_COEF40_41 0x1a9f
#define VD1_IF0_GEN_REG3 0x1aa7
#define VIU_OSD_BLENDO_H_START_END 0x1aa9
#define VIU_OSD_BLENDO_V_START_END 0x1aaa
#define VIU_OSD_BLEND_GEN_CTRL0 0x1aab
#define VIU_OSD_BLEND_GEN_CTRL1 0x1aac
#define VIU_OSD_BLEND_DUMMY_DATA 0x1aad
#define VIU_OSD_BLEND_CURRENT_XY 0x1aae
#define VIU_OSD2_MATRIX_CTRL 0x1ab0
#define VIU_OSD2_MATRIX_COEF00_01 0x1ab1
#define VIU_OSD2_MATRIX_COEF02_10 0x1ab2
#define VIU_OSD2_MATRIX_COEF11_12 0x1ab3
#define VIU_OSD2_MATRIX_COEF20_21 0x1ab4
#define VIU_OSD2_MATRIX_COEF22 0x1ab5
#define VIU_OSD2_MATRIX_OFFSET0_1 0x1ab6
#define VIU_OSD2_MATRIX_OFFSET2 0x1ab7
#define VIU_OSD2_MATRIX_PRE_OFFSET0_1 0x1ab8
#define VIU_OSD2_MATRIX_PRE_OFFSET2 0x1ab9
#define VIU_OSD2_MATRIX_PROBE_COLOR 0x1aba
#define VIU_OSD2_MATRIX_HL_COLOR 0x1abb
#define VIU_OSD2_MATRIX_PROBE_POS 0x1abc
#define VIU_OSD1_EOTF_CTL 0x1ad4
#define VIU_OSD1_EOTF_COEF00_01 0x1ad5
#define VIU_OSD1_EOTF_COEF02_10 0x1ad6
......@@ -481,6 +525,82 @@
#define VPP_OSD_SCALE_COEF 0x1dcd
#define VPP_INT_LINE_NUM 0x1dce
#define VPP_WRAP_OSD1_MATRIX_COEF00_01 0x3d60
#define VPP_WRAP_OSD1_MATRIX_COEF02_10 0x3d61
#define VPP_WRAP_OSD1_MATRIX_COEF11_12 0x3d62
#define VPP_WRAP_OSD1_MATRIX_COEF20_21 0x3d63
#define VPP_WRAP_OSD1_MATRIX_COEF22 0x3d64
#define VPP_WRAP_OSD1_MATRIX_COEF13_14 0x3d65
#define VPP_WRAP_OSD1_MATRIX_COEF23_24 0x3d66
#define VPP_WRAP_OSD1_MATRIX_COEF15_25 0x3d67
#define VPP_WRAP_OSD1_MATRIX_CLIP 0x3d68
#define VPP_WRAP_OSD1_MATRIX_OFFSET0_1 0x3d69
#define VPP_WRAP_OSD1_MATRIX_OFFSET2 0x3d6a
#define VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1 0x3d6b
#define VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2 0x3d6c
#define VPP_WRAP_OSD1_MATRIX_EN_CTRL 0x3d6d
#define VPP_WRAP_OSD2_MATRIX_COEF00_01 0x3d70
#define VPP_WRAP_OSD2_MATRIX_COEF02_10 0x3d71
#define VPP_WRAP_OSD2_MATRIX_COEF11_12 0x3d72
#define VPP_WRAP_OSD2_MATRIX_COEF20_21 0x3d73
#define VPP_WRAP_OSD2_MATRIX_COEF22 0x3d74
#define VPP_WRAP_OSD2_MATRIX_COEF13_14 0x3d75
#define VPP_WRAP_OSD2_MATRIX_COEF23_24 0x3d76
#define VPP_WRAP_OSD2_MATRIX_COEF15_25 0x3d77
#define VPP_WRAP_OSD2_MATRIX_CLIP 0x3d78
#define VPP_WRAP_OSD2_MATRIX_OFFSET0_1 0x3d79
#define VPP_WRAP_OSD2_MATRIX_OFFSET2 0x3d7a
#define VPP_WRAP_OSD2_MATRIX_PRE_OFFSET0_1 0x3d7b
#define VPP_WRAP_OSD2_MATRIX_PRE_OFFSET2 0x3d7c
#define VPP_WRAP_OSD2_MATRIX_EN_CTRL 0x3d7d
#define VPP_WRAP_OSD3_MATRIX_COEF00_01 0x3db0
#define VPP_WRAP_OSD3_MATRIX_COEF02_10 0x3db1
#define VPP_WRAP_OSD3_MATRIX_COEF11_12 0x3db2
#define VPP_WRAP_OSD3_MATRIX_COEF20_21 0x3db3
#define VPP_WRAP_OSD3_MATRIX_COEF22 0x3db4
#define VPP_WRAP_OSD3_MATRIX_COEF13_14 0x3db5
#define VPP_WRAP_OSD3_MATRIX_COEF23_24 0x3db6
#define VPP_WRAP_OSD3_MATRIX_COEF15_25 0x3db7
#define VPP_WRAP_OSD3_MATRIX_CLIP 0x3db8
#define VPP_WRAP_OSD3_MATRIX_OFFSET0_1 0x3db9
#define VPP_WRAP_OSD3_MATRIX_OFFSET2 0x3dba
#define VPP_WRAP_OSD3_MATRIX_PRE_OFFSET0_1 0x3dbb
#define VPP_WRAP_OSD3_MATRIX_PRE_OFFSET2 0x3dbc
#define VPP_WRAP_OSD3_MATRIX_EN_CTRL 0x3dbd
/* osd2 scaler */
#define OSD2_VSC_PHASE_STEP 0x3d00
#define OSD2_VSC_INI_PHASE 0x3d01
#define OSD2_VSC_CTRL0 0x3d02
#define OSD2_HSC_PHASE_STEP 0x3d03
#define OSD2_HSC_INI_PHASE 0x3d04
#define OSD2_HSC_CTRL0 0x3d05
#define OSD2_HSC_INI_PAT_CTRL 0x3d06
#define OSD2_SC_DUMMY_DATA 0x3d07
#define OSD2_SC_CTRL0 0x3d08
#define OSD2_SCI_WH_M1 0x3d09
#define OSD2_SCO_H_START_END 0x3d0a
#define OSD2_SCO_V_START_END 0x3d0b
#define OSD2_SCALE_COEF_IDX 0x3d18
#define OSD2_SCALE_COEF 0x3d19
/* osd34 scaler */
#define OSD34_SCALE_COEF_IDX 0x3d1e
#define OSD34_SCALE_COEF 0x3d1f
#define OSD34_VSC_PHASE_STEP 0x3d20
#define OSD34_VSC_INI_PHASE 0x3d21
#define OSD34_VSC_CTRL0 0x3d22
#define OSD34_HSC_PHASE_STEP 0x3d23
#define OSD34_HSC_INI_PHASE 0x3d24
#define OSD34_HSC_CTRL0 0x3d25
#define OSD34_HSC_INI_PAT_CTRL 0x3d26
#define OSD34_SC_DUMMY_DATA 0x3d27
#define OSD34_SC_CTRL0 0x3d28
#define OSD34_SCI_WH_M1 0x3d29
#define OSD34_SCO_H_START_END 0x3d2a
#define OSD34_SCO_V_START_END 0x3d2b
/* viu2 */
#define VIU2_ADDR_START 0x1e00
#define VIU2_ADDR_END 0x1eff
......@@ -1400,4 +1520,131 @@
#define OSDSR_YBIC_VCOEF0 0x3149
#define OSDSR_CBIC_VCOEF0 0x314a
/* osd afbcd on gxtvbb */
#define OSD1_AFBCD_ENABLE 0x31a0
#define OSD1_AFBCD_MODE 0x31a1
#define OSD1_AFBCD_SIZE_IN 0x31a2
#define OSD1_AFBCD_HDR_PTR 0x31a3
#define OSD1_AFBCD_FRAME_PTR 0x31a4
#define OSD1_AFBCD_CHROMA_PTR 0x31a5
#define OSD1_AFBCD_CONV_CTRL 0x31a6
#define OSD1_AFBCD_STATUS 0x31a8
#define OSD1_AFBCD_PIXEL_HSCOPE 0x31a9
#define OSD1_AFBCD_PIXEL_VSCOPE 0x31aa
#define VIU_MISC_CTRL1 0x1a07
/* add for gxm and 962e dv core2 */
#define DOLBY_CORE2A_SWAP_CTRL1 0x3434
#define DOLBY_CORE2A_SWAP_CTRL2 0x3435
/* osd afbc on g12a */
#define VPU_MAFBC_BLOCK_ID 0x3a00
#define VPU_MAFBC_IRQ_RAW_STATUS 0x3a01
#define VPU_MAFBC_IRQ_CLEAR 0x3a02
#define VPU_MAFBC_IRQ_MASK 0x3a03
#define VPU_MAFBC_IRQ_STATUS 0x3a04
#define VPU_MAFBC_COMMAND 0x3a05
#define VPU_MAFBC_STATUS 0x3a06
#define VPU_MAFBC_SURFACE_CFG 0x3a07
/* osd afbc on g12a */
#define VPU_MAFBC_HEADER_BUF_ADDR_LOW_S0 0x3a10
#define VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S0 0x3a11
#define VPU_MAFBC_FORMAT_SPECIFIER_S0 0x3a12
#define VPU_MAFBC_BUFFER_WIDTH_S0 0x3a13
#define VPU_MAFBC_BUFFER_HEIGHT_S0 0x3a14
#define VPU_MAFBC_BOUNDING_BOX_X_START_S0 0x3a15
#define VPU_MAFBC_BOUNDING_BOX_X_END_S0 0x3a16
#define VPU_MAFBC_BOUNDING_BOX_Y_START_S0 0x3a17
#define VPU_MAFBC_BOUNDING_BOX_Y_END_S0 0x3a18
#define VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S0 0x3a19
#define VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S0 0x3a1a
#define VPU_MAFBC_OUTPUT_BUF_STRIDE_S0 0x3a1b
#define VPU_MAFBC_PREFETCH_CFG_S0 0x3a1c
#define VPU_MAFBC_HEADER_BUF_ADDR_LOW_S1 0x3a30
#define VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S1 0x3a31
#define VPU_MAFBC_FORMAT_SPECIFIER_S1 0x3a32
#define VPU_MAFBC_BUFFER_WIDTH_S1 0x3a33
#define VPU_MAFBC_BUFFER_HEIGHT_S1 0x3a34
#define VPU_MAFBC_BOUNDING_BOX_X_START_S1 0x3a35
#define VPU_MAFBC_BOUNDING_BOX_X_END_S1 0x3a36
#define VPU_MAFBC_BOUNDING_BOX_Y_START_S1 0x3a37
#define VPU_MAFBC_BOUNDING_BOX_Y_END_S1 0x3a38
#define VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S1 0x3a39
#define VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S1 0x3a3a
#define VPU_MAFBC_OUTPUT_BUF_STRIDE_S1 0x3a3b
#define VPU_MAFBC_PREFETCH_CFG_S1 0x3a3c
#define VPU_MAFBC_HEADER_BUF_ADDR_LOW_S2 0x3a50
#define VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S2 0x3a51
#define VPU_MAFBC_FORMAT_SPECIFIER_S2 0x3a52
#define VPU_MAFBC_BUFFER_WIDTH_S2 0x3a53
#define VPU_MAFBC_BUFFER_HEIGHT_S2 0x3a54
#define VPU_MAFBC_BOUNDING_BOX_X_START_S2 0x3a55
#define VPU_MAFBC_BOUNDING_BOX_X_END_S2 0x3a56
#define VPU_MAFBC_BOUNDING_BOX_Y_START_S2 0x3a57
#define VPU_MAFBC_BOUNDING_BOX_Y_END_S2 0x3a58
#define VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S2 0x3a59
#define VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S2 0x3a5a
#define VPU_MAFBC_OUTPUT_BUF_STRIDE_S2 0x3a5b
#define VPU_MAFBC_PREFETCH_CFG_S2 0x3a5c
#define VPU_MAFBC_HEADER_BUF_ADDR_LOW_S3 0x3a70
#define VPU_MAFBC_HEADER_BUF_ADDR_HIGH_S3 0x3a71
#define VPU_MAFBC_FORMAT_SPECIFIER_S3 0x3a72
#define VPU_MAFBC_BUFFER_WIDTH_S3 0x3a73
#define VPU_MAFBC_BUFFER_HEIGHT_S3 0x3a74
#define VPU_MAFBC_BOUNDING_BOX_X_START_S3 0x3a75
#define VPU_MAFBC_BOUNDING_BOX_X_END_S3 0x3a76
#define VPU_MAFBC_BOUNDING_BOX_Y_START_S3 0x3a77
#define VPU_MAFBC_BOUNDING_BOX_Y_END_S3 0x3a78
#define VPU_MAFBC_OUTPUT_BUF_ADDR_LOW_S3 0x3a79
#define VPU_MAFBC_OUTPUT_BUF_ADDR_HIGH_S3 0x3a7a
#define VPU_MAFBC_OUTPUT_BUF_STRIDE_S3 0x3a7b
#define VPU_MAFBC_PREFETCH_CFG_S3 0x3a7c
#define DOLBY_PATH_CTRL 0x1a0c
#define OSD_PATH_MISC_CTRL 0x1a0e
#define MALI_AFBCD_TOP_CTRL 0x1a0f
#define VIU_OSD_BLEND_CTRL 0x39b0
#define VIU_OSD_BLEND_CTRL1 0x39c0
#define VIU_OSD_BLEND_DIN0_SCOPE_H 0x39b1
#define VIU_OSD_BLEND_DIN0_SCOPE_V 0x39b2
#define VIU_OSD_BLEND_DIN1_SCOPE_H 0x39b3
#define VIU_OSD_BLEND_DIN1_SCOPE_V 0x39b4
#define VIU_OSD_BLEND_DIN2_SCOPE_H 0x39b5
#define VIU_OSD_BLEND_DIN2_SCOPE_V 0x39b6
#define VIU_OSD_BLEND_DIN3_SCOPE_H 0x39b7
#define VIU_OSD_BLEND_DIN3_SCOPE_V 0x39b8
#define VIU_OSD_BLEND_DUMMY_DATA0 0x39b9
#define VIU_OSD_BLEND_DUMMY_ALPHA 0x39ba
#define VIU_OSD_BLEND_BLEND0_SIZE 0x39bb
#define VIU_OSD_BLEND_BLEND1_SIZE 0x39bc
#define VIU_OSD_BLEND_RO_CURRENT_XY 0x39bf
#define VPP_OUT_H_V_SIZE 0x1da5
#define VPP_VD2_HDR_IN_SIZE 0x1df0
#define VPP_OSD1_IN_SIZE 0x1df1
#define VPP_GCLK_CTRL2 0x1df2
#define VD2_PPS_DUMMY_DATA 0x1df4
#define VPP_OSD1_BLD_H_SCOPE 0x1df5
#define VPP_OSD1_BLD_V_SCOPE 0x1df6
#define VPP_OSD2_BLD_H_SCOPE 0x1df7
#define VPP_OSD2_BLD_V_SCOPE 0x1df8
#define VPP_WRBAK_CTRL 0x1df9
#define VPP_SLEEP_CTRL 0x1dfa
#define VD1_BLEND_SRC_CTRL 0x1dfb
#define VD2_BLEND_SRC_CTRL 0x1dfc
#define OSD1_BLEND_SRC_CTRL 0x1dfd
#define OSD2_BLEND_SRC_CTRL 0x1dfe
#define VPP_POST_BLEND_BLEND_DUMMY_DATA 0x3968
#define VPP_POST_BLEND_DUMMY_ALPHA 0x3969
#define VPP_RDARB_MODE 0x3978
#define VPP_RDARB_REQEN_SLV 0x3979
#define VPU_RDARB_MODE_L2C1 0x279d
#endif /* __MESON_REGISTERS_H */
......@@ -113,9 +113,12 @@
#define HHI_HDMI_PLL_CNTL4 0x32C /* 0xcb offset in data sheet */
#define HHI_HDMI_PLL_CNTL5 0x330 /* 0xcc offset in data sheet */
#define HHI_HDMI_PLL_CNTL6 0x334 /* 0xcd offset in data sheet */
#define HHI_HDMI_PLL_CNTL7 0x338 /* 0xce offset in data sheet */
#define HDMI_PLL_RESET BIT(28)
#define HDMI_PLL_RESET_G12A BIT(29)
#define HDMI_PLL_LOCK BIT(31)
#define HDMI_PLL_LOCK_G12A (3 << 30)
#define FREQ_1000_1001(_freq) DIV_ROUND_CLOSEST(_freq * 1000, 1001)
......@@ -257,6 +260,10 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4800023d);
/* Poll for lock bit */
regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
(val & HDMI_PLL_LOCK), 10, 0);
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b);
......@@ -271,11 +278,26 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
HDMI_PLL_RESET, HDMI_PLL_RESET);
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
HDMI_PLL_RESET, 0);
}
/* Poll for lock bit */
regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
(val & HDMI_PLL_LOCK), 10, 0);
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x1a0504f7);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00010000);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x00000000);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x6a28dc00);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x65771290);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x39272000);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL7, 0x56540000);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x3a0504f7);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x1a0504f7);
/* Poll for lock bit */
regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
((val & HDMI_PLL_LOCK_G12A) == HDMI_PLL_LOCK_G12A),
10, 0);
}
/* Disable VCLK2 */
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, 0);
......@@ -288,8 +310,13 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
VCLK2_DIV_MASK, (55 - 1));
/* select vid_pll for vclk2 */
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
VCLK2_SEL_MASK, (0 << VCLK2_SEL_SHIFT));
else
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
VCLK2_SEL_MASK, (4 << VCLK2_SEL_SHIFT));
/* enable vclk2 gate */
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, VCLK2_EN);
......@@ -396,8 +423,8 @@ struct meson_vclk_params {
},
[MESON_VCLK_HDMI_297000] = {
.pixel_freq = 297000,
.pll_base_freq = 2970000,
.pll_od1 = 1,
.pll_base_freq = 5940000,
.pll_od1 = 2,
.pll_od2 = 1,
.pll_od3 = 1,
.vid_pll_div = VID_PLL_DIV_5,
......@@ -476,6 +503,46 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
/* Poll for lock bit */
regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val,
(val & HDMI_PLL_LOCK), 10, 0);
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x0b3a0400 | m);
/* Enable and reset */
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
0x3 << 28, 0x3 << 28);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, frac);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x00000000);
/* G12A HDMI PLL Needs specific parameters for 5.4GHz */
if (m >= 0xf7) {
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0xea68dc00);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x65771290);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x39272000);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL7, 0x55540000);
} else {
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0a691c00);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x33771290);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x39270000);
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL7, 0x50540000);
}
do {
/* Reset PLL */
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
HDMI_PLL_RESET_G12A, HDMI_PLL_RESET_G12A);
/* UN-Reset PLL */
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
HDMI_PLL_RESET_G12A, 0);
/* Poll for lock bits */
if (!regmap_read_poll_timeout(priv->hhi,
HHI_HDMI_PLL_CNTL, val,
((val & HDMI_PLL_LOCK_G12A)
== HDMI_PLL_LOCK_G12A),
10, 100))
break;
} while(1);
}
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
......@@ -485,6 +552,9 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
3 << 21, pll_od_to_reg(od1) << 21);
else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
3 << 16, pll_od_to_reg(od1) << 16);
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
......@@ -493,6 +563,9 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
3 << 23, pll_od_to_reg(od2) << 23);
else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
3 << 18, pll_od_to_reg(od2) << 18);
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2,
......@@ -501,7 +574,9 @@ void meson_hdmi_pll_set_params(struct meson_drm *priv, unsigned int m,
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3,
3 << 19, pll_od_to_reg(od3) << 19);
else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
3 << 20, pll_od_to_reg(od3) << 20);
}
#define XTAL_FREQ 24000
......@@ -518,6 +593,7 @@ static unsigned int meson_hdmi_pll_get_m(struct meson_drm *priv,
#define HDMI_FRAC_MAX_GXBB 4096
#define HDMI_FRAC_MAX_GXL 1024
#define HDMI_FRAC_MAX_G12A 131072
static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
unsigned int m,
......@@ -534,6 +610,9 @@ static unsigned int meson_hdmi_pll_get_frac(struct meson_drm *priv,
parent_freq *= 2;
}
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
frac_max = HDMI_FRAC_MAX_G12A;
/* We can have a perfect match !*/
if (pll_freq / m == parent_freq &&
pll_freq % m == 0)
......@@ -559,7 +638,8 @@ static bool meson_hdmi_pll_validate_params(struct meson_drm *priv,
if (frac >= HDMI_FRAC_MAX_GXBB)
return false;
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu") ||
meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
/* Empiric supported min/max dividers */
if (m < 106 || m > 247)
return false;
......@@ -713,6 +793,23 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
break;
}
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
switch (pll_base_freq) {
case 2970000:
m = 0x7b;
frac = vic_alternate_clock ? 0x140b4 : 0x18000;
break;
case 4320000:
m = vic_alternate_clock ? 0xb3 : 0xb4;
frac = vic_alternate_clock ? 0x1a3ee : 0;
break;
case 5940000:
m = 0xf7;
frac = vic_alternate_clock ? 0x8148 : 0x10000;
break;
}
meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3);
}
......
......@@ -73,7 +73,9 @@
/* HHI Registers */
#define HHI_GCLK_MPEG2 0x148 /* 0x52 offset in data sheet */
#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */
#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */
#define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 offset in data sheet */
struct meson_cvbs_enci_mode meson_cvbs_enci_pal = {
......@@ -1675,8 +1677,13 @@ void meson_venc_disable_vsync(struct meson_drm *priv)
void meson_venc_init(struct meson_drm *priv)
{
/* Disable CVBS VDAC */
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 8);
} else {
regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8);
}
/* Power Down Dacs */
writel_relaxed(0xff, priv->io_base + _REG(VENC_VDAC_SETTING));
......
......@@ -37,7 +37,9 @@
/* HHI VDAC Registers */
#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
#define HHI_VDAC_CNTL0_G12A 0x2EC /* 0xbd offset in data sheet */
#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
#define HHI_VDAC_CNTL1_G12A 0x2F0 /* 0xbe offset in data sheet */
struct meson_venc_cvbs {
struct drm_encoder encoder;
......@@ -166,8 +168,13 @@ static void meson_venc_cvbs_encoder_disable(struct drm_encoder *encoder)
struct meson_drm *priv = meson_venc_cvbs->priv;
/* Disable CVBS VDAC */
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
} else {
regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8);
}
}
static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder)
......@@ -179,13 +186,17 @@ static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder)
/* VDAC0 source is not from ATV */
writel_bits_relaxed(BIT(5), 0, priv->io_base + _REG(VENC_VDAC_DACSEL0));
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1);
else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) {
regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001);
regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001);
regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
}
}
static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder,
......
......@@ -90,6 +90,34 @@ static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
EOTF_COEFF_RIGHTSHIFT /* right shift */
};
void meson_viu_set_g12a_osd1_matrix(struct meson_drm *priv, int *m,
bool csc_on)
{
/* VPP WRAP OSD1 matrix */
writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET0_1));
writel(m[2] & 0xfff,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_PRE_OFFSET2));
writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF00_01));
writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF02_10));
writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF11_12));
writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF20_21));
writel((m[11] & 0x1fff) << 16,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_COEF22));
writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET0_1));
writel(m[20] & 0xfff,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_OFFSET2));
writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
priv->io_base + _REG(VPP_WRAP_OSD1_MATRIX_EN_CTRL));
}
void meson_viu_set_osd_matrix(struct meson_drm *priv,
enum viu_matrix_sel_e m_select,
int *m, bool csc_on)
......@@ -336,11 +364,21 @@ void meson_viu_init(struct meson_drm *priv)
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
meson_viu_load_matrix(priv);
else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
meson_viu_set_g12a_osd1_matrix(priv, RGB709_to_YUV709l_coeff,
true);
/* Initialize OSD1 fifo control register */
reg = BIT(0) | /* Urgent DDR request priority */
(4 << 5) | /* hold_fifo_lines */
(3 << 10) | /* burst length 64 */
(4 << 5); /* hold_fifo_lines */
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
reg |= (1 << 10) | /* burst length 32 */
(32 << 12) | /* fifo_depth_val: 32*8=256 */
(2 << 22) | /* 4 words in 1 burst */
(2 << 24) |
(1 << 31);
else
reg |= (3 << 10) | /* burst length 64 */
(32 << 12) | /* fifo_depth_val: 32*8=256 */
(2 << 22) | /* 4 words in 1 burst */
(2 << 24);
......@@ -369,6 +407,30 @@ void meson_viu_init(struct meson_drm *priv)
writel_relaxed(0x00FF00C0,
priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE));
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
writel_relaxed(4 << 29 |
1 << 27 |
1 << 26 | /* blend_din0 input to blend0 */
1 << 25 | /* blend1_dout to blend2 */
1 << 24 | /* blend1_din3 input to blend1 */
1 << 20 |
0 << 16 |
1,
priv->io_base + _REG(VIU_OSD_BLEND_CTRL));
writel_relaxed(3 << 8 |
1 << 20,
priv->io_base + _REG(OSD1_BLEND_SRC_CTRL));
writel_relaxed(1 << 20,
priv->io_base + _REG(OSD2_BLEND_SRC_CTRL));
writel_relaxed(0, priv->io_base + _REG(VD1_BLEND_SRC_CTRL));
writel_relaxed(0, priv->io_base + _REG(VD2_BLEND_SRC_CTRL));
writel_relaxed(0,
priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_DATA0));
writel_relaxed(0,
priv->io_base + _REG(VIU_OSD_BLEND_DUMMY_ALPHA));
writel_bits_relaxed(0x3 << 2, 0x3 << 2,
priv->io_base + _REG(DOLBY_PATH_CTRL));
}
priv->viu.osd1_enabled = false;
priv->viu.osd1_commit = false;
......
......@@ -112,13 +112,19 @@ void meson_vpp_init(struct meson_drm *priv)
writel_relaxed(0x20000, priv->io_base + _REG(VPP_DOLBY_CTRL));
writel_relaxed(0x1020080,
priv->io_base + _REG(VPP_DUMMY_DATA1));
}
} else if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
writel_relaxed(0xf, priv->io_base + _REG(DOLBY_PATH_CTRL));
/* Initialize vpu fifo control registers */
if (meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu"))
writel_relaxed(0xfff << 20 | 0x1000,
priv->io_base + _REG(VPP_OFIFO_SIZE));
else
writel_relaxed(readl_relaxed(priv->io_base + _REG(VPP_OFIFO_SIZE)) |
0x77f, priv->io_base + _REG(VPP_OFIFO_SIZE));
writel_relaxed(0x08080808, priv->io_base + _REG(VPP_HOLD_LINES));
if (!meson_vpu_is_compatible(priv, "amlogic,meson-g12a-vpu")) {
/* Turn off preblend */
writel_bits_relaxed(VPP_PREBLEND_ENABLE, 0,
priv->io_base + _REG(VPP_MISC));
......@@ -138,6 +144,7 @@ void meson_vpp_init(struct meson_drm *priv)
priv->io_base + _REG(VPP_PREBLEND_VD1_H_START_END));
writel_relaxed(4096,
priv->io_base + _REG(VPP_BLEND_VD2_H_START_END));
}
/* Disable Scalers */
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
......
......@@ -123,7 +123,7 @@ static int jh057n_init_sequence(struct jh057n *ctx)
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret < 0) {
DRM_DEV_ERROR(dev, "Failed to exit sleep mode");
DRM_DEV_ERROR(dev, "Failed to exit sleep mode\n");
return ret;
}
/* Panel is operational 120 msec after reset */
......@@ -132,7 +132,7 @@ static int jh057n_init_sequence(struct jh057n *ctx)
if (ret)
return ret;
DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done");
DRM_DEV_DEBUG_DRIVER(dev, "Panel init sequence done\n");
return 0;
}
......@@ -172,7 +172,7 @@ static int jh057n_prepare(struct drm_panel *panel)
if (ctx->prepared)
return 0;
DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel.");
DRM_DEV_DEBUG_DRIVER(ctx->dev, "Resetting the panel\n");
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
usleep_range(20, 40);
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
......@@ -180,7 +180,8 @@ static int jh057n_prepare(struct drm_panel *panel)
ret = jh057n_init_sequence(ctx);
if (ret < 0) {
DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d", ret);
DRM_DEV_ERROR(ctx->dev, "Panel init sequence failed: %d\n",
ret);
return ret;
}
......@@ -212,7 +213,7 @@ static int jh057n_get_modes(struct drm_panel *panel)
mode = drm_mode_duplicate(panel->drm, &default_mode);
if (!mode) {
DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u",
DRM_DEV_ERROR(ctx->dev, "Failed to add mode %ux%u@%u\n",
default_mode.hdisplay, default_mode.vdisplay,
default_mode.vrefresh);
return -ENOMEM;
......@@ -241,7 +242,7 @@ static int allpixelson_set(void *data, u64 val)
struct jh057n *ctx = data;
struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on");
DRM_DEV_DEBUG_DRIVER(ctx->dev, "Setting all pixels on\n");
dsi_generic_write_seq(dsi, ST7703_CMD_ALL_PIXEL_ON);
msleep(val * 1000);
/* Reset the panel to get video back */
......@@ -290,7 +291,7 @@ static int jh057n_probe(struct mipi_dsi_device *dsi)
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset_gpio)) {
DRM_DEV_ERROR(dev, "cannot get reset gpio");
DRM_DEV_ERROR(dev, "cannot get reset gpio\n");
return PTR_ERR(ctx->reset_gpio);
}
......@@ -315,12 +316,12 @@ static int jh057n_probe(struct mipi_dsi_device *dsi)
ret = mipi_dsi_attach(dsi);
if (ret < 0) {
DRM_DEV_ERROR(dev, "mipi_dsi_attach failed. Is host ready?");
DRM_DEV_ERROR(dev, "mipi_dsi_attach failed. Is host ready?\n");
drm_panel_remove(&ctx->panel);
return ret;
}
DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready",
DRM_DEV_INFO(dev, "%ux%u@%u %ubpp dsi %udl - ready\n",
default_mode.hdisplay, default_mode.vdisplay,
default_mode.vrefresh,
mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes);
......
......@@ -330,6 +330,7 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
ret = vexpress_muxfpga_init();
if (ret) {
dev_err(dev, "unable to initialize muxfpga driver\n");
of_node_put(np);
return ret;
}
......@@ -337,17 +338,20 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv)
pdev = of_find_device_by_node(np);
if (!pdev) {
dev_err(dev, "can't find the sysreg device, deferring\n");
of_node_put(np);
return -EPROBE_DEFER;
}
map = dev_get_drvdata(&pdev->dev);
if (!map) {
dev_err(dev, "sysreg has not yet probed\n");
platform_device_put(pdev);
of_node_put(np);
return -EPROBE_DEFER;
}
} else {
map = syscon_node_to_regmap(np);
}
of_node_put(np);
if (IS_ERR(map)) {
dev_err(dev, "no Versatile syscon regmap\n");
......
......@@ -26,164 +26,6 @@ static unsigned int spi_max;
module_param(spi_max, uint, 0400);
MODULE_PARM_DESC(spi_max, "Set a lower SPI max transfer size");
/**
* tinydrm_memcpy - Copy clip buffer
* @dst: Destination buffer
* @vaddr: Source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
*/
void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip)
{
unsigned int cpp = drm_format_plane_cpp(fb->format->format, 0);
unsigned int pitch = fb->pitches[0];
void *src = vaddr + (clip->y1 * pitch) + (clip->x1 * cpp);
size_t len = (clip->x2 - clip->x1) * cpp;
unsigned int y;
for (y = clip->y1; y < clip->y2; y++) {
memcpy(dst, src, len);
src += pitch;
dst += len;
}
}
EXPORT_SYMBOL(tinydrm_memcpy);
/**
* tinydrm_swab16 - Swap bytes into clip buffer
* @dst: RGB565 destination buffer
* @vaddr: RGB565 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
*/
void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip)
{
size_t len = (clip->x2 - clip->x1) * sizeof(u16);
unsigned int x, y;
u16 *src, *buf;
/*
* The cma memory is write-combined so reads are uncached.
* Speed up by fetching one line at a time.
*/
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return;
for (y = clip->y1; y < clip->y2; y++) {
src = vaddr + (y * fb->pitches[0]);
src += clip->x1;
memcpy(buf, src, len);
src = buf;
for (x = clip->x1; x < clip->x2; x++)
*dst++ = swab16(*src++);
}
kfree(buf);
}
EXPORT_SYMBOL(tinydrm_swab16);
/**
* tinydrm_xrgb8888_to_rgb565 - Convert XRGB8888 to RGB565 clip buffer
* @dst: RGB565 destination buffer
* @vaddr: XRGB8888 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
* @swap: Swap bytes
*
* Drivers can use this function for RGB565 devices that don't natively
* support XRGB8888.
*/
void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr,
struct drm_framebuffer *fb,
struct drm_rect *clip, bool swap)
{
size_t len = (clip->x2 - clip->x1) * sizeof(u32);
unsigned int x, y;
u32 *src, *buf;
u16 val16;
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return;
for (y = clip->y1; y < clip->y2; y++) {
src = vaddr + (y * fb->pitches[0]);
src += clip->x1;
memcpy(buf, src, len);
src = buf;
for (x = clip->x1; x < clip->x2; x++) {
val16 = ((*src & 0x00F80000) >> 8) |
((*src & 0x0000FC00) >> 5) |
((*src & 0x000000F8) >> 3);
src++;
if (swap)
*dst++ = swab16(val16);
else
*dst++ = val16;
}
}
kfree(buf);
}
EXPORT_SYMBOL(tinydrm_xrgb8888_to_rgb565);
/**
* tinydrm_xrgb8888_to_gray8 - Convert XRGB8888 to grayscale
* @dst: 8-bit grayscale destination buffer
* @vaddr: XRGB8888 source buffer
* @fb: DRM framebuffer
* @clip: Clip rectangle area to copy
*
* Drm doesn't have native monochrome or grayscale support.
* Such drivers can announce the commonly supported XR24 format to userspace
* and use this function to convert to the native format.
*
* Monochrome drivers will use the most significant bit,
* where 1 means foreground color and 0 background color.
*
* ITU BT.601 is used for the RGB -> luma (brightness) conversion.
*/
void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip)
{
unsigned int len = (clip->x2 - clip->x1) * sizeof(u32);
unsigned int x, y;
void *buf;
u32 *src;
if (WARN_ON(fb->format->format != DRM_FORMAT_XRGB8888))
return;
/*
* The cma memory is write-combined so reads are uncached.
* Speed up by fetching one line at a time.
*/
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return;
for (y = clip->y1; y < clip->y2; y++) {
src = vaddr + (y * fb->pitches[0]);
src += clip->x1;
memcpy(buf, src, len);
src = buf;
for (x = clip->x1; x < clip->x2; x++) {
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 */
*dst++ = (3 * r + 6 * g + b) / 10;
src++;
}
}
kfree(buf);
}
EXPORT_SYMBOL(tinydrm_xrgb8888_to_gray8);
#if IS_ENABLED(CONFIG_SPI)
/**
......
......@@ -21,6 +21,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_vblank.h>
......@@ -218,12 +219,12 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
switch (fb->format->format) {
case DRM_FORMAT_RGB565:
if (swap)
tinydrm_swab16(dst, src, fb, clip);
drm_fb_swab16(dst, src, fb, clip);
else
tinydrm_memcpy(dst, src, fb, clip);
drm_fb_memcpy(dst, src, fb, clip);
break;
case DRM_FORMAT_XRGB8888:
tinydrm_xrgb8888_to_rgb565(dst, src, fb, clip, swap);
drm_fb_xrgb8888_to_rgb565(dst, src, fb, clip, swap);
break;
default:
dev_err_once(fb->dev->dev, "Format is not supported: %s\n",
......
......@@ -31,6 +31,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_rect.h>
......@@ -566,7 +567,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
goto out_free;
}
tinydrm_xrgb8888_to_gray8(buf, cma_obj->vaddr, fb, &clip);
drm_fb_xrgb8888_to_gray8(buf, cma_obj->vaddr, fb, &clip);
if (import_attach) {
ret = dma_buf_end_cpu_access(import_attach->dmabuf,
......
......@@ -22,6 +22,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_format_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_rect.h>
......@@ -77,7 +78,7 @@ static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr,
if (!buf)
return;
tinydrm_xrgb8888_to_gray8(buf, vaddr, fb, clip);
drm_fb_xrgb8888_to_gray8(buf, vaddr, fb, clip);
src = buf;
for (y = clip->y1; y < clip->y2; y++) {
......
......@@ -48,7 +48,7 @@ vc4_debugfs_init(struct drm_minor *minor)
return 0;
}
int vc4_debugfs_regset32(struct seq_file *m, void *unused)
static int vc4_debugfs_regset32(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *)m->private;
struct debugfs_regset32 *regset = node->info_ent->data;
......
/*
* Copyright (C) 2016 Noralf Trønnes
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __LINUX_DRM_FORMAT_HELPER_H
#define __LINUX_DRM_FORMAT_HELPER_H
struct drm_framebuffer;
struct drm_rect;
void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip);
void drm_fb_memcpy_dstclip(void *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip);
void drm_fb_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip);
void drm_fb_xrgb8888_to_rgb565(void *dst, void *vaddr,
struct drm_framebuffer *fb,
struct drm_rect *clip, bool swap);
void drm_fb_xrgb8888_to_rgb565_dstclip(void *dst, unsigned int dst_pitch,
void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip, bool swap);
void drm_fb_xrgb8888_to_rgb888_dstclip(void *dst, unsigned int dst_pitch,
void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip);
void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip);
#endif /* __LINUX_DRM_FORMAT_HELPER_H */
......@@ -46,16 +46,6 @@ int tinydrm_display_pipe_init(struct drm_device *drm,
const struct drm_display_mode *mode,
unsigned int rotation);
void tinydrm_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip);
void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip);
void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr,
struct drm_framebuffer *fb,
struct drm_rect *clip, bool swap);
void tinydrm_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb,
struct drm_rect *clip);
size_t tinydrm_spi_max_transfer_size(struct spi_device *spi, size_t max_len);
bool tinydrm_spi_bpw_supported(struct spi_device *spi, u8 bpw);
int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment