Add support for the Amlogic Meson Video Processing Unit
- Only CVBS/Composite output for Amlogic Meson GXBB/GXL/GXM SoCs - Add MAINTAINERS entry - Add DT bindings documentation -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJYRn0RAAoJEHfc29rIyEnR2PAQAIfgdJL1BiR/37BxFyR837e0 kFBlYcb4GDxfIT3ZKSrleullwDz7m7VoDmxmKRpq7UiDtt8e3CupD2jCU+NN0FY2 mFIlYSnCV+MSAeoakjfKqbP30n15iOw9s5ty5jbkkF4zLRn5vuLX8WSG0PbrYjZv 0Tcz6aavvusgqf830fCWt/+BxgYQURaE1iUYRc8qC7a4BoQVdZpuhP6v5waJTztW FaM8f7W45usxoejQ6gL095toHGY6UxuvV+kn9b7a6mW27eZZY3YhMRFnsywNTv/k 5NAa7vgEMYAb26nxSMcTeKnI7GxwyYQ9KXmtLB7RiGfC2/uIrNOSo8Gpu5IXpvRe ge/Zt8GXmqvHHkN64/LyloOQLSYVRWdhhz8KfPKmK/yaaZhjDUkqekq8PASlv5Rs 7dWsgTlTwioqByycwkJSWlNcu/UpNQxWS6FC8tiDmRE5CMiTQuFbnilij83W4nj0 cDiN4LWrW14gFGLnLpteMiaPFylIRtd9rd1AFEsXi5zM8FbyFmj+rKOvHiRwxf2h jW9UBDPj/g1BhNrjXYetPCxQoEYc0XIFpjXTqbd//F2lETCXPgDQybZ+nkrHoIfM ZEjc5ER2A25zo2k7aCxgtRkEDJM2HPEBqnqTEQ2kXNrZjYpkfa5SnHAzGASjcLfe 0efKP1+O05AWPHMbWjTE =rrKt -----END PGP SIGNATURE----- Merge tag 'meson-drm-for-4.10' of github.com:superna9999/linux into drm-next Add support for the Amlogic Meson Video Processing Unit - Only CVBS/Composite output for Amlogic Meson GXBB/GXL/GXM SoCs - Add MAINTAINERS entry - Add DT bindings documentation * tag 'meson-drm-for-4.10' of github.com:superna9999/linux: MAINTAINERS: add entry for Amlogic DRM drivers dt-bindings: display: add Amlogic Meson DRM Bindings drm: Add support for Amlogic Meson Graphic Controller
This commit is contained in:
commit
e783fd0cdc
|
@ -0,0 +1,112 @@
|
|||
Amlogic Meson Display Controller
|
||||
================================
|
||||
|
||||
The Amlogic Meson Display controller is composed of several components
|
||||
that are going to be documented below:
|
||||
|
||||
DMC|---------------VPU (Video Processing Unit)----------------|------HHI------|
|
||||
| vd1 _______ _____________ _________________ | |
|
||||
D |-------| |----| | | | | HDMI PLL |
|
||||
D | vd2 | VIU | | Video Post | | Video Encoders |<---|-----VCLK |
|
||||
R |-------| |----| Processing | | | | |
|
||||
| osd2 | | | |---| Enci ----------|----|-----VDAC------|
|
||||
R |-------| CSC |----| Scalers | | Encp ----------|----|----HDMI-TX----|
|
||||
A | osd1 | | | Blenders | | Encl ----------|----|---------------|
|
||||
M |-------|______|----|____________| |________________| | |
|
||||
___|__________________________________________________________|_______________|
|
||||
|
||||
|
||||
VIU: Video Input Unit
|
||||
---------------------
|
||||
|
||||
The Video Input Unit is in charge of the pixel scanout from the DDR memory.
|
||||
It fetches the frames addresses, stride and parameters from the "Canvas" memory.
|
||||
This part is also in charge of the CSC (Colorspace Conversion).
|
||||
It can handle 2 OSD Planes and 2 Video Planes.
|
||||
|
||||
VPP: Video Post Processing
|
||||
--------------------------
|
||||
|
||||
The Video Post Processing is in charge of the scaling and blending of the
|
||||
various planes into a single pixel stream.
|
||||
There is a special "pre-blending" used by the video planes with a dedicated
|
||||
scaler and a "post-blending" to merge with the OSD Planes.
|
||||
The OSD planes also have a dedicated scaler for one of the OSD.
|
||||
|
||||
VENC: Video Encoders
|
||||
--------------------
|
||||
|
||||
The VENC is composed of the multiple pixel encoders :
|
||||
- ENCI : Interlace Video encoder for CVBS and Interlace HDMI
|
||||
- ENCP : Progressive Video Encoder for HDMI
|
||||
- ENCL : LCD LVDS Encoder
|
||||
The VENC Unit gets a Pixel Clocks (VCLK) from a dedicated HDMI PLL and clock
|
||||
tree and provides the scanout clock to the VPP and VIU.
|
||||
The ENCI is connected to a single VDAC for Composite Output.
|
||||
The ENCI and ENCP are connected to an on-chip HDMI Transceiver.
|
||||
|
||||
Device Tree Bindings:
|
||||
---------------------
|
||||
|
||||
VPU: Video Processing Unit
|
||||
--------------------------
|
||||
|
||||
Required properties:
|
||||
- compatible: value should be different for each SoC family as :
|
||||
- GXBB (S905) : "amlogic,meson-gxbb-vpu"
|
||||
- GXL (S905X, S905D) : "amlogic,meson-gxl-vpu"
|
||||
- GXM (S912) : "amlogic,meson-gxm-vpu"
|
||||
followed by the common "amlogic,meson-gx-vpu"
|
||||
- reg: base address and size of he following memory-mapped regions :
|
||||
- vpu
|
||||
- hhi
|
||||
- dmc
|
||||
- reg-names: should contain the names of the previous memory regions
|
||||
- interrupts: should contain the VENC Vsync interrupt number
|
||||
|
||||
Required nodes:
|
||||
|
||||
The connections to the VPU output video ports are modeled using the OF graph
|
||||
bindings specified in Documentation/devicetree/bindings/graph.txt.
|
||||
|
||||
The following table lists for each supported model the port number
|
||||
corresponding to each VPU output.
|
||||
|
||||
Port 0 Port 1
|
||||
-----------------------------------------
|
||||
S905 (GXBB) CVBS VDAC HDMI-TX
|
||||
S905X (GXL) CVBS VDAC HDMI-TX
|
||||
S905D (GXL) CVBS VDAC HDMI-TX
|
||||
S912 (GXM) CVBS VDAC HDMI-TX
|
||||
|
||||
Example:
|
||||
|
||||
tv-connector {
|
||||
compatible = "composite-video-connector";
|
||||
|
||||
port {
|
||||
tv_connector_in: endpoint {
|
||||
remote-endpoint = <&cvbs_vdac_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
vpu: vpu@d0100000 {
|
||||
compatible = "amlogic,meson-gxbb-vpu";
|
||||
reg = <0x0 0xd0100000 0x0 0x100000>,
|
||||
<0x0 0xc883c000 0x0 0x1000>,
|
||||
<0x0 0xc8838000 0x0 0x1000>;
|
||||
reg-names = "vpu", "hhi", "dmc";
|
||||
interrupts = <GIC_SPI 3 IRQ_TYPE_EDGE_RISING>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
/* CVBS VDAC output port */
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
cvbs_vdac_out: endpoint {
|
||||
remote-endpoint = <&tv_connector_in>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -4121,6 +4121,15 @@ S: Supported
|
|||
F: drivers/gpu/drm/sun4i/
|
||||
F: Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
|
||||
|
||||
DRM DRIVERS FOR AMLOGIC SOCS
|
||||
M: Neil Armstrong <narmstrong@baylibre.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: linux-amlogic@lists.infradead.org
|
||||
W: http://linux-meson.com/
|
||||
S: Supported
|
||||
F: drivers/gpu/drm/meson/
|
||||
F: Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt
|
||||
|
||||
DRM DRIVERS FOR EXYNOS
|
||||
M: Inki Dae <inki.dae@samsung.com>
|
||||
M: Joonyoung Shim <jy0922.shim@samsung.com>
|
||||
|
|
|
@ -242,6 +242,8 @@ source "drivers/gpu/drm/zte/Kconfig"
|
|||
|
||||
source "drivers/gpu/drm/mxsfb/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/meson/Kconfig"
|
||||
|
||||
# Keep legacy drivers last
|
||||
|
||||
menuconfig DRM_LEGACY
|
||||
|
|
|
@ -81,6 +81,7 @@ obj-$(CONFIG_DRM_TEGRA) += tegra/
|
|||
obj-$(CONFIG_DRM_STI) += sti/
|
||||
obj-$(CONFIG_DRM_IMX) += imx/
|
||||
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
|
||||
obj-$(CONFIG_DRM_MESON) += meson/
|
||||
obj-y += i2c/
|
||||
obj-y += panel/
|
||||
obj-y += bridge/
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
config DRM_MESON
|
||||
tristate "DRM Support for Amlogic Meson Display Controller"
|
||||
depends on DRM && OF && (ARM || ARM64)
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_KMS_CMA_HELPER
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select VIDEOMODE_HELPERS
|
||||
select REGMAP_MMIO
|
|
@ -0,0 +1,4 @@
|
|||
meson-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o
|
||||
meson-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o
|
||||
|
||||
obj-$(CONFIG_DRM_MESON) += meson.o
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/*
|
||||
* CANVAS is a memory zone where physical memory frames information
|
||||
* are stored for the VIU to scanout.
|
||||
*/
|
||||
|
||||
/* DMC Registers */
|
||||
#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */
|
||||
#define CANVAS_WIDTH_LBIT 29
|
||||
#define CANVAS_WIDTH_LWID 3
|
||||
#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */
|
||||
#define CANVAS_WIDTH_HBIT 0
|
||||
#define CANVAS_HEIGHT_BIT 9
|
||||
#define CANVAS_BLKMODE_BIT 24
|
||||
#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */
|
||||
#define CANVAS_LUT_WR_EN (0x2 << 8)
|
||||
#define CANVAS_LUT_RD_EN (0x1 << 8)
|
||||
|
||||
void meson_canvas_setup(struct meson_drm *priv,
|
||||
uint32_t canvas_index, uint32_t addr,
|
||||
uint32_t stride, uint32_t height,
|
||||
unsigned int wrap,
|
||||
unsigned int blkmode)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_DATAL,
|
||||
(((addr + 7) >> 3)) |
|
||||
(((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_DATAH,
|
||||
((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
|
||||
CANVAS_WIDTH_HBIT) |
|
||||
(height << CANVAS_HEIGHT_BIT) |
|
||||
(wrap << 22) |
|
||||
(blkmode << CANVAS_BLKMODE_BIT));
|
||||
|
||||
regmap_write(priv->dmc, DMC_CAV_LUT_ADDR,
|
||||
CANVAS_LUT_WR_EN | canvas_index);
|
||||
|
||||
/* Force a read-back to make sure everything is flushed. */
|
||||
regmap_read(priv->dmc, DMC_CAV_LUT_DATAH, &val);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Canvas LUT Memory */
|
||||
|
||||
#ifndef __MESON_CANVAS_H
|
||||
#define __MESON_CANVAS_H
|
||||
|
||||
#define MESON_CANVAS_ID_OSD1 0x4e
|
||||
|
||||
/* Canvas configuration. */
|
||||
#define MESON_CANVAS_WRAP_NONE 0x00
|
||||
#define MESON_CANVAS_WRAP_X 0x01
|
||||
#define MESON_CANVAS_WRAP_Y 0x02
|
||||
|
||||
#define MESON_CANVAS_BLKMODE_LINEAR 0x00
|
||||
#define MESON_CANVAS_BLKMODE_32x32 0x01
|
||||
#define MESON_CANVAS_BLKMODE_64x64 0x02
|
||||
|
||||
void meson_canvas_setup(struct meson_drm *priv,
|
||||
uint32_t canvas_index, uint32_t addr,
|
||||
uint32_t stride, uint32_t height,
|
||||
unsigned int wrap,
|
||||
unsigned int blkmode);
|
||||
|
||||
#endif /* __MESON_CANVAS_H */
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_flip_work.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "meson_crtc.h"
|
||||
#include "meson_plane.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/* CRTC definition */
|
||||
|
||||
struct meson_crtc {
|
||||
struct drm_crtc base;
|
||||
struct drm_pending_vblank_event *event;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
#define to_meson_crtc(x) container_of(x, struct meson_crtc, base)
|
||||
|
||||
/* CRTC */
|
||||
|
||||
static const struct drm_crtc_funcs meson_crtc_funcs = {
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
};
|
||||
|
||||
static void meson_crtc_enable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
|
||||
struct drm_plane *plane = meson_crtc->priv->primary_plane;
|
||||
struct meson_drm *priv = meson_crtc->priv;
|
||||
|
||||
/* Enable VPP Postblend */
|
||||
writel(plane->state->crtc_w,
|
||||
priv->io_base + _REG(VPP_POSTBLEND_H_SIZE));
|
||||
|
||||
writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
priv->viu.osd1_enabled = true;
|
||||
}
|
||||
|
||||
static void meson_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
|
||||
struct meson_drm *priv = meson_crtc->priv;
|
||||
|
||||
priv->viu.osd1_enabled = false;
|
||||
|
||||
/* Disable VPP Postblend */
|
||||
writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
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_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
|
||||
unsigned long flags;
|
||||
|
||||
if (crtc->state->event) {
|
||||
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
|
||||
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
meson_crtc->event = crtc->state->event;
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void meson_crtc_atomic_flush(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(crtc);
|
||||
struct meson_drm *priv = meson_crtc->priv;
|
||||
|
||||
if (priv->viu.osd1_enabled)
|
||||
priv->viu.osd1_commit = true;
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = {
|
||||
.enable = meson_crtc_enable,
|
||||
.disable = meson_crtc_disable,
|
||||
.atomic_begin = meson_crtc_atomic_begin,
|
||||
.atomic_flush = meson_crtc_atomic_flush,
|
||||
};
|
||||
|
||||
void meson_crtc_irq(struct meson_drm *priv)
|
||||
{
|
||||
struct meson_crtc *meson_crtc = to_meson_crtc(priv->crtc);
|
||||
unsigned long flags;
|
||||
|
||||
/* Update the OSD registers */
|
||||
if (priv->viu.osd1_enabled && priv->viu.osd1_commit) {
|
||||
writel_relaxed(priv->viu.osd1_ctrl_stat,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[0],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[1],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W1));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[2],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W2));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[3],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3));
|
||||
writel_relaxed(priv->viu.osd1_blk0_cfg[4],
|
||||
priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4));
|
||||
|
||||
/* If output is interlace, make use of the Scaler */
|
||||
if (priv->viu.osd1_interlace) {
|
||||
struct drm_plane *plane = priv->primary_plane;
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_rect dest = {
|
||||
.x1 = state->crtc_x,
|
||||
.y1 = state->crtc_y,
|
||||
.x2 = state->crtc_x + state->crtc_w,
|
||||
.y2 = state->crtc_y + state->crtc_h,
|
||||
};
|
||||
|
||||
meson_vpp_setup_interlace_vscaler_osd1(priv, &dest);
|
||||
} else
|
||||
meson_vpp_disable_interlace_vscaler_osd1(priv);
|
||||
|
||||
/* Enable OSD1 */
|
||||
writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
priv->viu.osd1_commit = false;
|
||||
}
|
||||
|
||||
drm_crtc_handle_vblank(priv->crtc);
|
||||
|
||||
spin_lock_irqsave(&priv->drm->event_lock, flags);
|
||||
if (meson_crtc->event) {
|
||||
drm_crtc_send_vblank_event(priv->crtc, meson_crtc->event);
|
||||
drm_crtc_vblank_put(priv->crtc);
|
||||
meson_crtc->event = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->drm->event_lock, flags);
|
||||
}
|
||||
|
||||
int meson_crtc_create(struct meson_drm *priv)
|
||||
{
|
||||
struct meson_crtc *meson_crtc;
|
||||
struct drm_crtc *crtc;
|
||||
int ret;
|
||||
|
||||
meson_crtc = devm_kzalloc(priv->drm->dev, sizeof(*meson_crtc),
|
||||
GFP_KERNEL);
|
||||
if (!meson_crtc)
|
||||
return -ENOMEM;
|
||||
|
||||
meson_crtc->priv = priv;
|
||||
crtc = &meson_crtc->base;
|
||||
ret = drm_crtc_init_with_planes(priv->drm, crtc,
|
||||
priv->primary_plane, NULL,
|
||||
&meson_crtc_funcs, "meson_crtc");
|
||||
if (ret) {
|
||||
dev_err(priv->drm->dev, "Failed to init CRTC\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_crtc_helper_add(crtc, &meson_crtc_helper_funcs);
|
||||
|
||||
priv->crtc = crtc;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_CRTC_H
|
||||
#define __MESON_CRTC_H
|
||||
|
||||
#include "meson_drv.h"
|
||||
|
||||
int meson_crtc_create(struct meson_drm *priv);
|
||||
|
||||
void meson_crtc_irq(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_CRTC_H */
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_flip_work.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_rect.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
|
||||
#include "meson_drv.h"
|
||||
#include "meson_plane.h"
|
||||
#include "meson_crtc.h"
|
||||
#include "meson_venc_cvbs.h"
|
||||
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
#define DRIVER_NAME "meson"
|
||||
#define DRIVER_DESC "Amlogic Meson DRM driver"
|
||||
|
||||
/*
|
||||
* Video Processing Unit
|
||||
*
|
||||
* VPU Handles the Global Video Processing, it includes management of the
|
||||
* clocks gates, blocks reset lines and power domains.
|
||||
*
|
||||
* What is missing :
|
||||
* - Full reset of entire video processing HW blocks
|
||||
* - Scaling and setup of the VPU clock
|
||||
* - Bus clock gates
|
||||
* - Powering up video processing HW blocks
|
||||
* - Powering Up HDMI controller and PHY
|
||||
*/
|
||||
|
||||
static void meson_fb_output_poll_changed(struct drm_device *dev)
|
||||
{
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
drm_fbdev_cma_hotplug_event(priv->fbdev);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs meson_mode_config_funcs = {
|
||||
.output_poll_changed = meson_fb_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
.fb_create = drm_fb_cma_create,
|
||||
};
|
||||
|
||||
static int meson_enable_vblank(struct drm_device *dev, unsigned int crtc)
|
||||
{
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
meson_venc_enable_vsync(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_disable_vblank(struct drm_device *dev, unsigned int crtc)
|
||||
{
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
meson_venc_disable_vsync(priv);
|
||||
}
|
||||
|
||||
static irqreturn_t meson_irq(int irq, void *arg)
|
||||
{
|
||||
struct drm_device *dev = arg;
|
||||
struct meson_drm *priv = dev->dev_private;
|
||||
|
||||
(void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
|
||||
|
||||
meson_crtc_irq(priv);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct file_operations fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = drm_compat_ioctl,
|
||||
#endif
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.llseek = no_llseek,
|
||||
.mmap = drm_gem_cma_mmap,
|
||||
};
|
||||
|
||||
static struct drm_driver meson_driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM |
|
||||
DRIVER_MODESET | DRIVER_PRIME |
|
||||
DRIVER_ATOMIC,
|
||||
|
||||
/* Vblank */
|
||||
.enable_vblank = meson_enable_vblank,
|
||||
.disable_vblank = meson_disable_vblank,
|
||||
.get_vblank_counter = drm_vblank_no_hw_counter,
|
||||
|
||||
/* IRQ */
|
||||
.irq_handler = meson_irq,
|
||||
|
||||
/* PRIME Ops */
|
||||
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
|
||||
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
|
||||
.gem_prime_import = drm_gem_prime_import,
|
||||
.gem_prime_export = drm_gem_prime_export,
|
||||
.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
|
||||
.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
|
||||
.gem_prime_vmap = drm_gem_cma_prime_vmap,
|
||||
.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
|
||||
.gem_prime_mmap = drm_gem_cma_prime_mmap,
|
||||
|
||||
/* GEM Ops */
|
||||
.dumb_create = drm_gem_cma_dumb_create,
|
||||
.dumb_destroy = drm_gem_dumb_destroy,
|
||||
.dumb_map_offset = drm_gem_cma_dumb_map_offset,
|
||||
.gem_free_object_unlocked = drm_gem_cma_free_object,
|
||||
.gem_vm_ops = &drm_gem_cma_vm_ops,
|
||||
|
||||
/* Misc */
|
||||
.fops = &fops,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = "20161109",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
};
|
||||
|
||||
static bool meson_vpu_has_available_connectors(struct device *dev)
|
||||
{
|
||||
struct device_node *ep, *remote;
|
||||
|
||||
/* Parses each endpoint and check if remote exists */
|
||||
for_each_endpoint_of_node(dev->of_node, ep) {
|
||||
/* If the endpoint node exists, consider it enabled */
|
||||
remote = of_graph_get_remote_port(ep);
|
||||
if (remote)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct regmap_config meson_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x1000,
|
||||
};
|
||||
|
||||
static int meson_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct meson_drm *priv;
|
||||
struct drm_device *drm;
|
||||
struct resource *res;
|
||||
void __iomem *regs;
|
||||
int ret;
|
||||
|
||||
/* Checks if an output connector is available */
|
||||
if (!meson_vpu_has_available_connectors(dev)) {
|
||||
dev_err(dev, "No output connector available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drm = drm_dev_alloc(&meson_driver, dev);
|
||||
if (IS_ERR(drm))
|
||||
return PTR_ERR(drm);
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto free_drm;
|
||||
}
|
||||
drm->dev_private = priv;
|
||||
priv->drm = drm;
|
||||
priv->dev = dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu");
|
||||
regs = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
priv->io_base = regs;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi");
|
||||
/* Simply ioremap since it may be a shared register zone */
|
||||
regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!regs)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
priv->hhi = devm_regmap_init_mmio(dev, regs,
|
||||
&meson_regmap_config);
|
||||
if (IS_ERR(priv->hhi)) {
|
||||
dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
|
||||
return PTR_ERR(priv->hhi);
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc");
|
||||
/* Simply ioremap since it may be a shared register zone */
|
||||
regs = devm_ioremap(dev, res->start, resource_size(res));
|
||||
if (!regs)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
priv->dmc = devm_regmap_init_mmio(dev, regs,
|
||||
&meson_regmap_config);
|
||||
if (IS_ERR(priv->dmc)) {
|
||||
dev_err(&pdev->dev, "Couldn't create the DMC regmap\n");
|
||||
return PTR_ERR(priv->dmc);
|
||||
}
|
||||
|
||||
priv->vsync_irq = platform_get_irq(pdev, 0);
|
||||
|
||||
drm_vblank_init(drm, 1);
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
/* Encoder Initialization */
|
||||
|
||||
ret = meson_venc_cvbs_create(priv);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
/* Hardware Initialization */
|
||||
|
||||
meson_venc_init(priv);
|
||||
meson_vpp_init(priv);
|
||||
meson_viu_init(priv);
|
||||
|
||||
ret = meson_plane_create(priv);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
ret = meson_crtc_create(priv);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
ret = drm_irq_install(drm, priv->vsync_irq);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
drm->mode_config.max_width = 8192;
|
||||
drm->mode_config.max_height = 8192;
|
||||
drm->mode_config.funcs = &meson_mode_config_funcs;
|
||||
|
||||
priv->fbdev = drm_fbdev_cma_init(drm, 32,
|
||||
drm->mode_config.num_crtc,
|
||||
drm->mode_config.num_connector);
|
||||
if (IS_ERR(priv->fbdev)) {
|
||||
ret = PTR_ERR(priv->fbdev);
|
||||
goto free_drm;
|
||||
}
|
||||
|
||||
drm_kms_helper_poll_init(drm);
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = drm_dev_register(drm, 0);
|
||||
if (ret)
|
||||
goto free_drm;
|
||||
|
||||
return 0;
|
||||
|
||||
free_drm:
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_device *drm = dev_get_drvdata(&pdev->dev);
|
||||
struct meson_drm *priv = drm->dev_private;
|
||||
|
||||
drm_dev_unregister(drm);
|
||||
drm_kms_helper_poll_fini(drm);
|
||||
drm_fbdev_cma_fini(priv->fbdev);
|
||||
drm_mode_config_cleanup(drm);
|
||||
drm_vblank_cleanup(drm);
|
||||
drm_dev_unref(drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dt_match[] = {
|
||||
{ .compatible = "amlogic,meson-gxbb-vpu" },
|
||||
{ .compatible = "amlogic,meson-gxl-vpu" },
|
||||
{ .compatible = "amlogic,meson-gxm-vpu" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dt_match);
|
||||
|
||||
static struct platform_driver meson_drm_platform_driver = {
|
||||
.probe = meson_drv_probe,
|
||||
.remove = meson_drv_remove,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = dt_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(meson_drm_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MESON_DRV_H
|
||||
#define __MESON_DRV_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
struct meson_drm {
|
||||
struct device *dev;
|
||||
void __iomem *io_base;
|
||||
struct regmap *hhi;
|
||||
struct regmap *dmc;
|
||||
int vsync_irq;
|
||||
|
||||
struct drm_device *drm;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_fbdev_cma *fbdev;
|
||||
struct drm_plane *primary_plane;
|
||||
|
||||
/* Components Data */
|
||||
struct {
|
||||
bool osd1_enabled;
|
||||
bool osd1_interlace;
|
||||
bool osd1_commit;
|
||||
uint32_t osd1_ctrl_stat;
|
||||
uint32_t osd1_blk0_cfg[5];
|
||||
} viu;
|
||||
|
||||
struct {
|
||||
unsigned int current_mode;
|
||||
} venc;
|
||||
};
|
||||
|
||||
static inline int meson_vpu_is_compatible(struct meson_drm *priv,
|
||||
const char *compat)
|
||||
{
|
||||
return of_device_is_compatible(priv->dev->of_node, compat);
|
||||
}
|
||||
|
||||
#endif /* __MESON_DRV_H */
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_rect.h>
|
||||
|
||||
#include "meson_plane.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
struct meson_plane {
|
||||
struct drm_plane base;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
#define to_meson_plane(x) container_of(x, struct meson_plane, base)
|
||||
|
||||
static int meson_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_rect clip = { 0, };
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
clip.x2 = crtc_state->mode.hdisplay;
|
||||
clip.y2 = crtc_state->mode.vdisplay;
|
||||
|
||||
return drm_plane_helper_check_state(state, &clip,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
DRM_PLANE_HELPER_NO_SCALING,
|
||||
true, true);
|
||||
}
|
||||
|
||||
/* Takes a fixed 16.16 number and converts it to integer. */
|
||||
static inline int64_t fixed16_to_int(int64_t value)
|
||||
{
|
||||
return value >> 16;
|
||||
}
|
||||
|
||||
static void meson_plane_atomic_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct meson_plane *meson_plane = to_meson_plane(plane);
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct meson_drm *priv = meson_plane->priv;
|
||||
struct drm_gem_cma_object *gem;
|
||||
struct drm_rect src = {
|
||||
.x1 = (state->src_x),
|
||||
.y1 = (state->src_y),
|
||||
.x2 = (state->src_x + state->src_w),
|
||||
.y2 = (state->src_y + state->src_h),
|
||||
};
|
||||
struct drm_rect dest = {
|
||||
.x1 = state->crtc_x,
|
||||
.y1 = state->crtc_y,
|
||||
.x2 = state->crtc_x + state->crtc_w,
|
||||
.y2 = state->crtc_y + state->crtc_h,
|
||||
};
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Update Coordinates
|
||||
* Update Formats
|
||||
* Update Buffer
|
||||
* Enable Plane
|
||||
*/
|
||||
spin_lock_irqsave(&priv->drm->event_lock, flags);
|
||||
|
||||
/* Enable OSD and BLK0, set max global alpha */
|
||||
priv->viu.osd1_ctrl_stat = OSD_ENABLE |
|
||||
(0xFF << OSD_GLOBAL_ALPHA_SHIFT) |
|
||||
OSD_BLK0_ENABLE;
|
||||
|
||||
/* Set up BLK0 to point to the right canvas */
|
||||
priv->viu.osd1_blk0_cfg[0] = ((MESON_CANVAS_ID_OSD1 << OSD_CANVAS_SEL) |
|
||||
OSD_ENDIANNESS_LE);
|
||||
|
||||
/* On GXBB, Use the old non-HDR RGB2YUV converter */
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu"))
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB;
|
||||
|
||||
switch (fb->pixel_format) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
/* For XRGB, replace the pixel's alpha by 0xFF */
|
||||
writel_bits_relaxed(OSD_REPLACE_EN, OSD_REPLACE_EN,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 |
|
||||
OSD_COLOR_MATRIX_32_ARGB;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
/* For ARGB, use the pixel's alpha */
|
||||
writel_bits_relaxed(OSD_REPLACE_EN, 0,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 |
|
||||
OSD_COLOR_MATRIX_32_ARGB;
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_24 |
|
||||
OSD_COLOR_MATRIX_24_RGB;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_16 |
|
||||
OSD_COLOR_MATRIX_16_RGB565;
|
||||
break;
|
||||
};
|
||||
|
||||
if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
priv->viu.osd1_interlace = true;
|
||||
|
||||
dest.y1 /= 2;
|
||||
dest.y2 /= 2;
|
||||
} else
|
||||
priv->viu.osd1_interlace = false;
|
||||
|
||||
/*
|
||||
* The format of these registers is (x2 << 16 | x1),
|
||||
* where x2 is exclusive.
|
||||
* e.g. +30x1920 would be (1919 << 16) | 30
|
||||
*/
|
||||
priv->viu.osd1_blk0_cfg[1] = ((fixed16_to_int(src.x2) - 1) << 16) |
|
||||
fixed16_to_int(src.x1);
|
||||
priv->viu.osd1_blk0_cfg[2] = ((fixed16_to_int(src.y2) - 1) << 16) |
|
||||
fixed16_to_int(src.y1);
|
||||
priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1;
|
||||
priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1;
|
||||
|
||||
/* Update Canvas with buffer address */
|
||||
gem = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
|
||||
meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1,
|
||||
gem->paddr, fb->pitches[0],
|
||||
fb->height, MESON_CANVAS_WRAP_NONE,
|
||||
MESON_CANVAS_BLKMODE_LINEAR);
|
||||
|
||||
spin_unlock_irqrestore(&priv->drm->event_lock, flags);
|
||||
}
|
||||
|
||||
static void meson_plane_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct meson_plane *meson_plane = to_meson_plane(plane);
|
||||
struct meson_drm *priv = meson_plane->priv;
|
||||
|
||||
/* Disable OSD1 */
|
||||
writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs meson_plane_helper_funcs = {
|
||||
.atomic_check = meson_plane_atomic_check,
|
||||
.atomic_disable = meson_plane_atomic_disable,
|
||||
.atomic_update = meson_plane_atomic_update,
|
||||
};
|
||||
|
||||
static const struct drm_plane_funcs meson_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = drm_plane_cleanup,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
};
|
||||
|
||||
static const uint32_t supported_drm_formats[] = {
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_RGB888,
|
||||
DRM_FORMAT_RGB565,
|
||||
};
|
||||
|
||||
int meson_plane_create(struct meson_drm *priv)
|
||||
{
|
||||
struct meson_plane *meson_plane;
|
||||
struct drm_plane *plane;
|
||||
|
||||
meson_plane = devm_kzalloc(priv->drm->dev, sizeof(*meson_plane),
|
||||
GFP_KERNEL);
|
||||
if (!meson_plane)
|
||||
return -ENOMEM;
|
||||
|
||||
meson_plane->priv = priv;
|
||||
plane = &meson_plane->base;
|
||||
|
||||
drm_universal_plane_init(priv->drm, plane, 0xFF,
|
||||
&meson_plane_funcs,
|
||||
supported_drm_formats,
|
||||
ARRAY_SIZE(supported_drm_formats),
|
||||
DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane");
|
||||
|
||||
drm_plane_helper_add(plane, &meson_plane_helper_funcs);
|
||||
|
||||
priv->primary_plane = plane;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_PLANE_H
|
||||
#define __MESON_PLANE_H
|
||||
|
||||
#include "meson_drv.h"
|
||||
|
||||
int meson_plane_create(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_PLANE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_vclk.h"
|
||||
|
||||
/*
|
||||
* VCLK is the "Pixel Clock" frequency generator from a dedicated PLL.
|
||||
* We handle the following encodings :
|
||||
* - CVBS 27MHz generator via the VCLK2 to the VENCI and VDAC blocks
|
||||
*
|
||||
* What is missing :
|
||||
* - HDMI Pixel Clocks generation
|
||||
*/
|
||||
|
||||
/* HHI Registers */
|
||||
#define HHI_VID_PLL_CLK_DIV 0x1a0 /* 0x68 offset in data sheet */
|
||||
#define VID_PLL_EN BIT(19)
|
||||
#define VID_PLL_BYPASS BIT(18)
|
||||
#define VID_PLL_PRESET BIT(15)
|
||||
#define HHI_VIID_CLK_DIV 0x128 /* 0x4a offset in data sheet */
|
||||
#define VCLK2_DIV_MASK 0xff
|
||||
#define VCLK2_DIV_EN BIT(16)
|
||||
#define VCLK2_DIV_RESET BIT(17)
|
||||
#define CTS_VDAC_SEL_MASK (0xf << 28)
|
||||
#define CTS_VDAC_SEL_SHIFT 28
|
||||
#define HHI_VIID_CLK_CNTL 0x12c /* 0x4b offset in data sheet */
|
||||
#define VCLK2_EN BIT(19)
|
||||
#define VCLK2_SEL_MASK (0x7 << 16)
|
||||
#define VCLK2_SEL_SHIFT 16
|
||||
#define VCLK2_SOFT_RESET BIT(15)
|
||||
#define VCLK2_DIV1_EN BIT(0)
|
||||
#define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */
|
||||
#define CTS_ENCI_SEL_MASK (0xf << 28)
|
||||
#define CTS_ENCI_SEL_SHIFT 28
|
||||
#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */
|
||||
#define CTS_ENCI_EN BIT(0)
|
||||
#define CTS_VDAC_EN BIT(4)
|
||||
|
||||
#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
|
||||
#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
|
||||
|
||||
#define HHI_HDMI_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */
|
||||
#define HHI_HDMI_PLL_CNTL2 0x324 /* 0xc9 offset in data sheet */
|
||||
#define HHI_HDMI_PLL_CNTL3 0x328 /* 0xca offset in data sheet */
|
||||
#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 HDMI_PLL_RESET BIT(28)
|
||||
#define HDMI_PLL_LOCK BIT(31)
|
||||
|
||||
/*
|
||||
* Setup VCLK2 for 27MHz, and enable clocks for ENCI and VDAC
|
||||
*
|
||||
* TOFIX: Refactor into table to also handle HDMI frequency and paths
|
||||
*/
|
||||
static void meson_venci_cvbs_clock_config(struct meson_drm *priv)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* Setup PLL to output 1.485GHz */
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) {
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00404e00);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c);
|
||||
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);
|
||||
} 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);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0xa6212844);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c4d000c);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729);
|
||||
regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500);
|
||||
|
||||
/* Reset PLL */
|
||||
regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL,
|
||||
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);
|
||||
|
||||
/* Disable VCLK2 */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, 0);
|
||||
|
||||
/* Disable vid_pll output clock */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_EN, 0);
|
||||
regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_PRESET, 0);
|
||||
/* Enable vid_pll bypass to HDMI pll */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV,
|
||||
VID_PLL_BYPASS, VID_PLL_BYPASS);
|
||||
/* Enable the vid_pll output clock */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV,
|
||||
VID_PLL_EN, VID_PLL_EN);
|
||||
|
||||
/* Setup the VCLK2 divider value to achieve 27MHz */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV,
|
||||
VCLK2_DIV_MASK, (55 - 1));
|
||||
|
||||
/* select vid_pll for vclk2 */
|
||||
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);
|
||||
|
||||
/* select vclk_div1 for enci */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV,
|
||||
CTS_ENCI_SEL_MASK, (8 << CTS_ENCI_SEL_SHIFT));
|
||||
/* select vclk_div1 for vdac */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV,
|
||||
CTS_VDAC_SEL_MASK, (8 << CTS_VDAC_SEL_SHIFT));
|
||||
|
||||
/* release vclk2_div_reset and enable vclk2_div */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV,
|
||||
VCLK2_DIV_EN | VCLK2_DIV_RESET, VCLK2_DIV_EN);
|
||||
|
||||
/* enable vclk2_div1 gate */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
|
||||
VCLK2_DIV1_EN, VCLK2_DIV1_EN);
|
||||
|
||||
/* reset vclk2 */
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
|
||||
VCLK2_SOFT_RESET, VCLK2_SOFT_RESET);
|
||||
regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL,
|
||||
VCLK2_SOFT_RESET, 0);
|
||||
|
||||
/* enable enci_clk */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2,
|
||||
CTS_ENCI_EN, CTS_ENCI_EN);
|
||||
/* enable vdac_clk */
|
||||
regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2,
|
||||
CTS_VDAC_EN, CTS_VDAC_EN);
|
||||
}
|
||||
|
||||
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
|
||||
unsigned int freq)
|
||||
{
|
||||
if (target == MESON_VCLK_TARGET_CVBS && freq == MESON_VCLK_CVBS)
|
||||
meson_venci_cvbs_clock_config(priv);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Video Clock */
|
||||
|
||||
#ifndef __MESON_VCLK_H
|
||||
#define __MESON_VCLK_H
|
||||
|
||||
enum {
|
||||
MESON_VCLK_TARGET_CVBS = 0,
|
||||
};
|
||||
|
||||
/* 27MHz is the CVBS Pixel Clock */
|
||||
#define MESON_VCLK_CVBS 27000
|
||||
|
||||
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
|
||||
unsigned int freq);
|
||||
|
||||
#endif /* __MESON_VCLK_H */
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_vclk.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/*
|
||||
* VENC Handle the pixels encoding to the output formats.
|
||||
* We handle the following encodings :
|
||||
* - CVBS Encoding via the ENCI encoder and VDAC digital to analog converter
|
||||
*
|
||||
* What is missing :
|
||||
* - TMDS/HDMI Encoding via ENCI_DIV and ENCP
|
||||
* - Setup of more clock rates for HDMI modes
|
||||
* - LCD Panel encoding via ENCL
|
||||
* - TV Panel encoding via ENCT
|
||||
*/
|
||||
|
||||
struct meson_cvbs_enci_mode meson_cvbs_enci_pal = {
|
||||
.mode_tag = MESON_VENC_MODE_CVBS_PAL,
|
||||
.hso_begin = 3,
|
||||
.hso_end = 129,
|
||||
.vso_even = 3,
|
||||
.vso_odd = 260,
|
||||
.macv_max_amp = 7,
|
||||
.video_prog_mode = 0xff,
|
||||
.video_mode = 0x13,
|
||||
.sch_adjust = 0x28,
|
||||
.yc_delay = 0x343,
|
||||
.pixel_start = 251,
|
||||
.pixel_end = 1691,
|
||||
.top_field_line_start = 22,
|
||||
.top_field_line_end = 310,
|
||||
.bottom_field_line_start = 23,
|
||||
.bottom_field_line_end = 311,
|
||||
.video_saturation = 9,
|
||||
.video_contrast = 0,
|
||||
.video_brightness = 0,
|
||||
.video_hue = 0,
|
||||
.analog_sync_adj = 0x8080,
|
||||
};
|
||||
|
||||
struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc = {
|
||||
.mode_tag = MESON_VENC_MODE_CVBS_NTSC,
|
||||
.hso_begin = 5,
|
||||
.hso_end = 129,
|
||||
.vso_even = 3,
|
||||
.vso_odd = 260,
|
||||
.macv_max_amp = 0xb,
|
||||
.video_prog_mode = 0xf0,
|
||||
.video_mode = 0x8,
|
||||
.sch_adjust = 0x20,
|
||||
.yc_delay = 0x333,
|
||||
.pixel_start = 227,
|
||||
.pixel_end = 1667,
|
||||
.top_field_line_start = 18,
|
||||
.top_field_line_end = 258,
|
||||
.bottom_field_line_start = 19,
|
||||
.bottom_field_line_end = 259,
|
||||
.video_saturation = 18,
|
||||
.video_contrast = 3,
|
||||
.video_brightness = 0,
|
||||
.video_hue = 0,
|
||||
.analog_sync_adj = 0x9c00,
|
||||
};
|
||||
|
||||
void meson_venci_cvbs_mode_set(struct meson_drm *priv,
|
||||
struct meson_cvbs_enci_mode *mode)
|
||||
{
|
||||
if (mode->mode_tag == priv->venc.current_mode)
|
||||
return;
|
||||
|
||||
/* CVBS Filter settings */
|
||||
writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL));
|
||||
writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL2));
|
||||
|
||||
/* Digital Video Select : Interlace, clk27 clk, external */
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_DVI_SETTING));
|
||||
|
||||
/* Reset Video Mode */
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE));
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV));
|
||||
|
||||
/* Horizontal sync signal output */
|
||||
writel_relaxed(mode->hso_begin,
|
||||
priv->io_base + _REG(ENCI_SYNC_HSO_BEGIN));
|
||||
writel_relaxed(mode->hso_end,
|
||||
priv->io_base + _REG(ENCI_SYNC_HSO_END));
|
||||
|
||||
/* Vertical Sync lines */
|
||||
writel_relaxed(mode->vso_even,
|
||||
priv->io_base + _REG(ENCI_SYNC_VSO_EVNLN));
|
||||
writel_relaxed(mode->vso_odd,
|
||||
priv->io_base + _REG(ENCI_SYNC_VSO_ODDLN));
|
||||
|
||||
/* Macrovision max amplitude change */
|
||||
writel_relaxed(0x8100 + mode->macv_max_amp,
|
||||
priv->io_base + _REG(ENCI_MACV_MAX_AMP));
|
||||
|
||||
/* Video mode */
|
||||
writel_relaxed(mode->video_prog_mode,
|
||||
priv->io_base + _REG(VENC_VIDEO_PROG_MODE));
|
||||
writel_relaxed(mode->video_mode,
|
||||
priv->io_base + _REG(ENCI_VIDEO_MODE));
|
||||
|
||||
/* Advanced Video Mode :
|
||||
* Demux shifting 0x2
|
||||
* Blank line end at line17/22
|
||||
* High bandwidth Luma Filter
|
||||
* Low bandwidth Chroma Filter
|
||||
* Bypass luma low pass filter
|
||||
* No macrovision on CSYNC
|
||||
*/
|
||||
writel_relaxed(0x26, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV));
|
||||
|
||||
writel(mode->sch_adjust, priv->io_base + _REG(ENCI_VIDEO_SCH));
|
||||
|
||||
/* Sync mode : MASTER Master mode, free run, send HSO/VSO out */
|
||||
writel_relaxed(0x07, priv->io_base + _REG(ENCI_SYNC_MODE));
|
||||
|
||||
/* 0x3 Y, C, and Component Y delay */
|
||||
writel_relaxed(mode->yc_delay, priv->io_base + _REG(ENCI_YC_DELAY));
|
||||
|
||||
/* Timings */
|
||||
writel_relaxed(mode->pixel_start,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_START));
|
||||
writel_relaxed(mode->pixel_end,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_END));
|
||||
|
||||
writel_relaxed(mode->top_field_line_start,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_START));
|
||||
writel_relaxed(mode->top_field_line_end,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_END));
|
||||
|
||||
writel_relaxed(mode->bottom_field_line_start,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_START));
|
||||
writel_relaxed(mode->bottom_field_line_end,
|
||||
priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_END));
|
||||
|
||||
/* Internal Venc, Internal VIU Sync, Internal Vencoder */
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_SYNC_ROUTE));
|
||||
|
||||
/* UNreset Interlaced TV Encoder */
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_DBG_PX_RST));
|
||||
|
||||
/* Enable Vfifo2vd, Y_Cb_Y_Cr select */
|
||||
writel_relaxed(0x4e01, priv->io_base + _REG(ENCI_VFIFO2VD_CTL));
|
||||
|
||||
/* Power UP Dacs */
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_SETTING));
|
||||
|
||||
/* Video Upsampling */
|
||||
writel_relaxed(0x0061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL0));
|
||||
writel_relaxed(0x4061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL1));
|
||||
writel_relaxed(0x5061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL2));
|
||||
|
||||
/* Select Interlace Y DACs */
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL1));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL2));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL3));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL4));
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL5));
|
||||
|
||||
/* Select ENCI for VIU */
|
||||
meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCI);
|
||||
|
||||
/* Enable ENCI FIFO */
|
||||
writel_relaxed(0x2000, priv->io_base + _REG(VENC_VDAC_FIFO_CTRL));
|
||||
|
||||
/* Select ENCI DACs 0, 1, 4, and 5 */
|
||||
writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_0));
|
||||
writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_1));
|
||||
|
||||
/* Interlace video enable */
|
||||
writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
|
||||
|
||||
/* Configure Video Saturation / Contrast / Brightness / Hue */
|
||||
writel_relaxed(mode->video_saturation,
|
||||
priv->io_base + _REG(ENCI_VIDEO_SAT));
|
||||
writel_relaxed(mode->video_contrast,
|
||||
priv->io_base + _REG(ENCI_VIDEO_CONT));
|
||||
writel_relaxed(mode->video_brightness,
|
||||
priv->io_base + _REG(ENCI_VIDEO_BRIGHT));
|
||||
writel_relaxed(mode->video_hue,
|
||||
priv->io_base + _REG(ENCI_VIDEO_HUE));
|
||||
|
||||
/* Enable DAC0 Filter */
|
||||
writel_relaxed(0x1, priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL0));
|
||||
writel_relaxed(0xfc48, priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL1));
|
||||
|
||||
/* 0 in Macrovision register 0 */
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_MACV_N0));
|
||||
|
||||
/* Analog Synchronization and color burst value adjust */
|
||||
writel_relaxed(mode->analog_sync_adj,
|
||||
priv->io_base + _REG(ENCI_SYNC_ADJ));
|
||||
|
||||
/* Setup 27MHz vclk2 for ENCI and VDAC */
|
||||
meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS);
|
||||
|
||||
priv->venc.current_mode = mode->mode_tag;
|
||||
}
|
||||
|
||||
/* Returns the current ENCI field polarity */
|
||||
unsigned int meson_venci_get_field(struct meson_drm *priv)
|
||||
{
|
||||
return readl_relaxed(priv->io_base + _REG(ENCI_INFO_READ)) & BIT(29);
|
||||
}
|
||||
|
||||
void meson_venc_enable_vsync(struct meson_drm *priv)
|
||||
{
|
||||
writel_relaxed(2, priv->io_base + _REG(VENC_INTCTRL));
|
||||
}
|
||||
|
||||
void meson_venc_disable_vsync(struct meson_drm *priv)
|
||||
{
|
||||
writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL));
|
||||
}
|
||||
|
||||
void meson_venc_init(struct meson_drm *priv)
|
||||
{
|
||||
/* Disable all encoders */
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN));
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
|
||||
writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN));
|
||||
|
||||
/* Disable VSync IRQ */
|
||||
meson_venc_disable_vsync(priv);
|
||||
|
||||
priv->venc.current_mode = MESON_VENC_MODE_NONE;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Video Encoders
|
||||
* - ENCI : Interlace Video Encoder
|
||||
* - ENCI_DVI : Interlace Video Encoder for DVI/HDMI
|
||||
* - ENCP : Progressive Video Encoder
|
||||
*/
|
||||
|
||||
#ifndef __MESON_VENC_H
|
||||
#define __MESON_VENC_H
|
||||
|
||||
enum {
|
||||
MESON_VENC_MODE_NONE = 0,
|
||||
MESON_VENC_MODE_CVBS_PAL,
|
||||
MESON_VENC_MODE_CVBS_NTSC,
|
||||
};
|
||||
|
||||
struct meson_cvbs_enci_mode {
|
||||
unsigned int mode_tag;
|
||||
unsigned int hso_begin; /* HSO begin position */
|
||||
unsigned int hso_end; /* HSO end position */
|
||||
unsigned int vso_even; /* VSO even line */
|
||||
unsigned int vso_odd; /* VSO odd line */
|
||||
unsigned int macv_max_amp; /* Macrovision max amplitude */
|
||||
unsigned int video_prog_mode;
|
||||
unsigned int video_mode;
|
||||
unsigned int sch_adjust;
|
||||
unsigned int yc_delay;
|
||||
unsigned int pixel_start;
|
||||
unsigned int pixel_end;
|
||||
unsigned int top_field_line_start;
|
||||
unsigned int top_field_line_end;
|
||||
unsigned int bottom_field_line_start;
|
||||
unsigned int bottom_field_line_end;
|
||||
unsigned int video_saturation;
|
||||
unsigned int video_contrast;
|
||||
unsigned int video_brightness;
|
||||
unsigned int video_hue;
|
||||
unsigned int analog_sync_adj;
|
||||
};
|
||||
|
||||
/* CVBS Timings and Parameters */
|
||||
extern struct meson_cvbs_enci_mode meson_cvbs_enci_pal;
|
||||
extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc;
|
||||
|
||||
void meson_venci_cvbs_mode_set(struct meson_drm *priv,
|
||||
struct meson_cvbs_enci_mode *mode);
|
||||
unsigned int meson_venci_get_field(struct meson_drm *priv);
|
||||
|
||||
void meson_venc_enable_vsync(struct meson_drm *priv);
|
||||
void meson_venc_disable_vsync(struct meson_drm *priv);
|
||||
|
||||
void meson_venc_init(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_VENC_H */
|
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_graph.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
|
||||
#include "meson_venc_cvbs.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/* HHI VDAC Registers */
|
||||
#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */
|
||||
#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */
|
||||
|
||||
struct meson_venc_cvbs {
|
||||
struct drm_encoder encoder;
|
||||
struct drm_connector connector;
|
||||
struct meson_drm *priv;
|
||||
};
|
||||
#define encoder_to_meson_venc_cvbs(x) \
|
||||
container_of(x, struct meson_venc_cvbs, encoder)
|
||||
|
||||
#define connector_to_meson_venc_cvbs(x) \
|
||||
container_of(x, struct meson_venc_cvbs, connector)
|
||||
|
||||
/* Supported Modes */
|
||||
|
||||
struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = {
|
||||
{ /* PAL */
|
||||
.enci = &meson_cvbs_enci_pal,
|
||||
.mode = {
|
||||
DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
|
||||
720, 732, 795, 864, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_INTERLACE),
|
||||
.vrefresh = 50,
|
||||
.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
|
||||
},
|
||||
},
|
||||
{ /* NTSC */
|
||||
.enci = &meson_cvbs_enci_ntsc,
|
||||
.mode = {
|
||||
DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
|
||||
720, 739, 801, 858, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_INTERLACE),
|
||||
.vrefresh = 60,
|
||||
.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* Connector */
|
||||
|
||||
static void meson_cvbs_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
meson_cvbs_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
/* FIXME: Add load-detect or jack-detect if possible */
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static int meson_cvbs_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_display_mode *mode;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
|
||||
struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
|
||||
|
||||
mode = drm_mode_duplicate(dev, &meson_mode->mode);
|
||||
if (!mode) {
|
||||
DRM_ERROR("Failed to create a new display mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
drm_mode_probed_add(connector, mode);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int meson_cvbs_connector_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
/* Validate the modes added in get_modes */
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs meson_cvbs_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.detect = meson_cvbs_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = meson_cvbs_connector_destroy,
|
||||
.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 const
|
||||
struct drm_connector_helper_funcs meson_cvbs_connector_helper_funcs = {
|
||||
.get_modes = meson_cvbs_connector_get_modes,
|
||||
.mode_valid = meson_cvbs_connector_mode_valid,
|
||||
};
|
||||
|
||||
/* Encoder */
|
||||
|
||||
static void meson_venc_cvbs_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs meson_venc_cvbs_encoder_funcs = {
|
||||
.destroy = meson_venc_cvbs_encoder_destroy,
|
||||
};
|
||||
|
||||
static int meson_venc_cvbs_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
|
||||
struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
|
||||
|
||||
if (drm_mode_equal(&crtc_state->mode, &meson_mode->mode))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void meson_venc_cvbs_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct meson_venc_cvbs *meson_venc_cvbs =
|
||||
encoder_to_meson_venc_cvbs(encoder);
|
||||
struct meson_drm *priv = meson_venc_cvbs->priv;
|
||||
|
||||
/* Disable CVBS VDAC */
|
||||
regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
|
||||
regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
|
||||
}
|
||||
|
||||
static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct meson_venc_cvbs *meson_venc_cvbs =
|
||||
encoder_to_meson_venc_cvbs(encoder);
|
||||
struct meson_drm *priv = meson_venc_cvbs->priv;
|
||||
|
||||
/* 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"))
|
||||
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_CNTL0, 0xf0001);
|
||||
|
||||
regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
|
||||
}
|
||||
|
||||
static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct meson_venc_cvbs *meson_venc_cvbs =
|
||||
encoder_to_meson_venc_cvbs(encoder);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
|
||||
struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
|
||||
|
||||
if (drm_mode_equal(mode, &meson_mode->mode)) {
|
||||
meson_venci_cvbs_mode_set(meson_venc_cvbs->priv,
|
||||
meson_mode->enci);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs
|
||||
meson_venc_cvbs_encoder_helper_funcs = {
|
||||
.atomic_check = meson_venc_cvbs_encoder_atomic_check,
|
||||
.disable = meson_venc_cvbs_encoder_disable,
|
||||
.enable = meson_venc_cvbs_encoder_enable,
|
||||
.mode_set = meson_venc_cvbs_encoder_mode_set,
|
||||
};
|
||||
|
||||
static bool meson_venc_cvbs_connector_is_available(struct meson_drm *priv)
|
||||
{
|
||||
struct device_node *ep, *remote;
|
||||
|
||||
/* CVBS VDAC output is on the first port, first endpoint */
|
||||
ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0);
|
||||
if (!ep)
|
||||
return false;
|
||||
|
||||
|
||||
/* If the endpoint node exists, consider it enabled */
|
||||
remote = of_graph_get_remote_port(ep);
|
||||
if (remote) {
|
||||
of_node_put(ep);
|
||||
return true;
|
||||
}
|
||||
|
||||
of_node_put(ep);
|
||||
of_node_put(remote);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int meson_venc_cvbs_create(struct meson_drm *priv)
|
||||
{
|
||||
struct drm_device *drm = priv->drm;
|
||||
struct meson_venc_cvbs *meson_venc_cvbs;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
int ret;
|
||||
|
||||
if (!meson_venc_cvbs_connector_is_available(priv)) {
|
||||
dev_info(drm->dev, "CVBS Output connector not available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
meson_venc_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_venc_cvbs),
|
||||
GFP_KERNEL);
|
||||
if (!meson_venc_cvbs)
|
||||
return -ENOMEM;
|
||||
|
||||
meson_venc_cvbs->priv = priv;
|
||||
encoder = &meson_venc_cvbs->encoder;
|
||||
connector = &meson_venc_cvbs->connector;
|
||||
|
||||
/* Connector */
|
||||
|
||||
drm_connector_helper_add(connector,
|
||||
&meson_cvbs_connector_helper_funcs);
|
||||
|
||||
ret = drm_connector_init(drm, connector, &meson_cvbs_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_Composite);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to init CVBS connector\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
connector->interlace_allowed = 1;
|
||||
|
||||
/* Encoder */
|
||||
|
||||
drm_encoder_helper_add(encoder, &meson_venc_cvbs_encoder_helper_funcs);
|
||||
|
||||
ret = drm_encoder_init(drm, encoder, &meson_venc_cvbs_encoder_funcs,
|
||||
DRM_MODE_ENCODER_TVDAC, "meson_venc_cvbs");
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "Failed to init CVBS encoder\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
encoder->possible_crtcs = BIT(0);
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Written by:
|
||||
* Jasper St. Pierre <jstpierre@mecheye.net>
|
||||
*/
|
||||
|
||||
#ifndef __MESON_VENC_CVBS_H
|
||||
#define __MESON_VENC_CVBS_H
|
||||
|
||||
#include "meson_drv.h"
|
||||
#include "meson_venc.h"
|
||||
|
||||
struct meson_cvbs_mode {
|
||||
struct meson_cvbs_enci_mode *enci;
|
||||
struct drm_display_mode mode;
|
||||
};
|
||||
|
||||
#define MESON_CVBS_MODES_COUNT 2
|
||||
|
||||
/* Modes supported by the CVBS output */
|
||||
extern struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT];
|
||||
|
||||
int meson_venc_cvbs_create(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_VENC_CVBS_H */
|
|
@ -0,0 +1,331 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_viu.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_venc.h"
|
||||
#include "meson_canvas.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/*
|
||||
* VIU Handles the Pixel scanout and the basic Colorspace conversions
|
||||
* We handle the following features :
|
||||
* - OSD1 RGB565/RGB888/xRGB8888 scanout
|
||||
* - RGB conversion to x/cb/cr
|
||||
* - Progressive or Interlace buffer scanout
|
||||
* - OSD1 Commit on Vsync
|
||||
* - HDR OSD matrix for GXL/GXM
|
||||
*
|
||||
* What is missing :
|
||||
* - BGR888/xBGR8888/BGRx8888/BGRx8888 modes
|
||||
* - YUV4:2:2 Y0CbY1Cr scanout
|
||||
* - Conversion to YUV 4:4:4 from 4:2:2 input
|
||||
* - Colorkey Alpha matching
|
||||
* - Big endian scanout
|
||||
* - X/Y reverse scanout
|
||||
* - Global alpha setup
|
||||
* - OSD2 support, would need interlace switching on vsync
|
||||
* - OSD1 full scaling to support TV overscan
|
||||
*/
|
||||
|
||||
/* OSD csc defines */
|
||||
|
||||
enum viu_matrix_sel_e {
|
||||
VIU_MATRIX_OSD_EOTF = 0,
|
||||
VIU_MATRIX_OSD,
|
||||
};
|
||||
|
||||
enum viu_lut_sel_e {
|
||||
VIU_LUT_OSD_EOTF = 0,
|
||||
VIU_LUT_OSD_OETF,
|
||||
};
|
||||
|
||||
#define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2))
|
||||
#define MATRIX_5X3_COEF_SIZE 24
|
||||
|
||||
#define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2))
|
||||
#define EOTF_COEFF_SIZE 10
|
||||
#define EOTF_COEFF_RIGHTSHIFT 1
|
||||
|
||||
static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = {
|
||||
0, 0, 0, /* pre offset */
|
||||
COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765),
|
||||
COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500),
|
||||
COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116),
|
||||
0, 0, 0, /* 10'/11'/12' */
|
||||
0, 0, 0, /* 20'/21'/22' */
|
||||
64, 512, 512, /* offset */
|
||||
0, 0, 0 /* mode, right_shift, clip_en */
|
||||
};
|
||||
|
||||
/* eotf matrix: bypass */
|
||||
static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = {
|
||||
EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0),
|
||||
EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0),
|
||||
EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0),
|
||||
EOTF_COEFF_RIGHTSHIFT /* right shift */
|
||||
};
|
||||
|
||||
void meson_viu_set_osd_matrix(struct meson_drm *priv,
|
||||
enum viu_matrix_sel_e m_select,
|
||||
int *m, bool csc_on)
|
||||
{
|
||||
if (m_select == VIU_MATRIX_OSD) {
|
||||
/* osd matrix, VIU_MATRIX_0 */
|
||||
writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1));
|
||||
writel(m[2] & 0xfff,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2));
|
||||
writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01));
|
||||
writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10));
|
||||
writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12));
|
||||
writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21));
|
||||
|
||||
if (m[21]) {
|
||||
writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff),
|
||||
priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COEF22_30));
|
||||
writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff),
|
||||
priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COEF31_32));
|
||||
writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff),
|
||||
priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COEF40_41));
|
||||
writel(m[17] & 0x1fff, priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
|
||||
} else
|
||||
writel((m[11] & 0x1fff) << 16, priv->io_base +
|
||||
_REG(VIU_OSD1_MATRIX_COEF22_30));
|
||||
|
||||
writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff),
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1));
|
||||
writel(m[20] & 0xfff,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2));
|
||||
|
||||
writel_bits_relaxed(3 << 30, m[21] << 30,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
|
||||
writel_bits_relaxed(7 << 16, m[22] << 16,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42));
|
||||
|
||||
/* 23 reserved for clipping control */
|
||||
writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
|
||||
writel_bits_relaxed(BIT(1), 0,
|
||||
priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL));
|
||||
} else if (m_select == VIU_MATRIX_OSD_EOTF) {
|
||||
int i;
|
||||
|
||||
/* osd eotf matrix, VIU_MATRIX_OSD_EOTF */
|
||||
for (i = 0; i < 5; i++)
|
||||
writel(((m[i * 2] & 0x1fff) << 16) |
|
||||
(m[i * 2 + 1] & 0x1fff), priv->io_base +
|
||||
_REG(VIU_OSD1_EOTF_CTL + i + 1));
|
||||
|
||||
writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0,
|
||||
priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
|
||||
writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0,
|
||||
priv->io_base + _REG(VIU_OSD1_EOTF_CTL));
|
||||
}
|
||||
}
|
||||
|
||||
#define OSD_EOTF_LUT_SIZE 33
|
||||
#define OSD_OETF_LUT_SIZE 41
|
||||
|
||||
void meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel,
|
||||
unsigned int *r_map, unsigned int *g_map,
|
||||
unsigned int *b_map,
|
||||
bool csc_on)
|
||||
{
|
||||
unsigned int addr_port;
|
||||
unsigned int data_port;
|
||||
unsigned int ctrl_port;
|
||||
int i;
|
||||
|
||||
if (lut_sel == VIU_LUT_OSD_EOTF) {
|
||||
addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT;
|
||||
data_port = VIU_OSD1_EOTF_LUT_DATA_PORT;
|
||||
ctrl_port = VIU_OSD1_EOTF_CTL;
|
||||
} else if (lut_sel == VIU_LUT_OSD_OETF) {
|
||||
addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT;
|
||||
data_port = VIU_OSD1_OETF_LUT_DATA_PORT;
|
||||
ctrl_port = VIU_OSD1_OETF_CTL;
|
||||
} else
|
||||
return;
|
||||
|
||||
if (lut_sel == VIU_LUT_OSD_OETF) {
|
||||
writel(0, priv->io_base + _REG(addr_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
writel(b_map[OSD_OETF_LUT_SIZE - 1],
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
if (csc_on)
|
||||
writel_bits_relaxed(0x7 << 29, 7 << 29,
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
else
|
||||
writel_bits_relaxed(0x7 << 29, 0,
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
} else if (lut_sel == VIU_LUT_OSD_EOTF) {
|
||||
writel(0, priv->io_base + _REG(addr_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
for (i = 0; i < 20; i++)
|
||||
writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16),
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
writel(b_map[OSD_EOTF_LUT_SIZE - 1],
|
||||
priv->io_base + _REG(data_port));
|
||||
|
||||
if (csc_on)
|
||||
writel_bits_relaxed(7 << 27, 7 << 27,
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
else
|
||||
writel_bits_relaxed(7 << 27, 0,
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
|
||||
writel_bits_relaxed(BIT(31), BIT(31),
|
||||
priv->io_base + _REG(ctrl_port));
|
||||
}
|
||||
}
|
||||
|
||||
/* eotf lut: linear */
|
||||
static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = {
|
||||
0x0000, 0x0200, 0x0400, 0x0600,
|
||||
0x0800, 0x0a00, 0x0c00, 0x0e00,
|
||||
0x1000, 0x1200, 0x1400, 0x1600,
|
||||
0x1800, 0x1a00, 0x1c00, 0x1e00,
|
||||
0x2000, 0x2200, 0x2400, 0x2600,
|
||||
0x2800, 0x2a00, 0x2c00, 0x2e00,
|
||||
0x3000, 0x3200, 0x3400, 0x3600,
|
||||
0x3800, 0x3a00, 0x3c00, 0x3e00,
|
||||
0x4000
|
||||
};
|
||||
|
||||
/* osd oetf lut: linear */
|
||||
static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = {
|
||||
0, 0, 0, 0,
|
||||
0, 32, 64, 96,
|
||||
128, 160, 196, 224,
|
||||
256, 288, 320, 352,
|
||||
384, 416, 448, 480,
|
||||
512, 544, 576, 608,
|
||||
640, 672, 704, 736,
|
||||
768, 800, 832, 864,
|
||||
896, 928, 960, 992,
|
||||
1023, 1023, 1023, 1023,
|
||||
1023
|
||||
};
|
||||
|
||||
static void meson_viu_load_matrix(struct meson_drm *priv)
|
||||
{
|
||||
/* eotf lut bypass */
|
||||
meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF,
|
||||
eotf_33_linear_mapping, /* R */
|
||||
eotf_33_linear_mapping, /* G */
|
||||
eotf_33_linear_mapping, /* B */
|
||||
false);
|
||||
|
||||
/* eotf matrix bypass */
|
||||
meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF,
|
||||
eotf_bypass_coeff,
|
||||
false);
|
||||
|
||||
/* oetf lut bypass */
|
||||
meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF,
|
||||
oetf_41_linear_mapping, /* R */
|
||||
oetf_41_linear_mapping, /* G */
|
||||
oetf_41_linear_mapping, /* B */
|
||||
false);
|
||||
|
||||
/* osd matrix RGB709 to YUV709 limit */
|
||||
meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD,
|
||||
RGB709_to_YUV709l_coeff,
|
||||
true);
|
||||
}
|
||||
|
||||
void meson_viu_init(struct meson_drm *priv)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Disable OSDs */
|
||||
writel_bits_relaxed(BIT(0) | BIT(21), 0,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT));
|
||||
writel_bits_relaxed(BIT(0) | BIT(21), 0,
|
||||
priv->io_base + _REG(VIU_OSD2_CTRL_STAT));
|
||||
|
||||
/* On GXL/GXM, Use the 10bit HDR conversion matrix */
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") ||
|
||||
meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
|
||||
meson_viu_load_matrix(priv);
|
||||
|
||||
/* Initialize OSD1 fifo control register */
|
||||
reg = BIT(0) | /* Urgent DDR request priority */
|
||||
(4 << 5) | /* hold_fifo_lines */
|
||||
(3 << 10) | /* burst length 64 */
|
||||
(32 << 12) | /* fifo_depth_val: 32*8=256 */
|
||||
(2 << 22) | /* 4 words in 1 burst */
|
||||
(2 << 24);
|
||||
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
|
||||
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
|
||||
|
||||
/* Set OSD alpha replace value */
|
||||
writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
|
||||
0xff << OSD_REPLACE_SHIFT,
|
||||
priv->io_base + _REG(VIU_OSD1_CTRL_STAT2));
|
||||
writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT,
|
||||
0xff << OSD_REPLACE_SHIFT,
|
||||
priv->io_base + _REG(VIU_OSD2_CTRL_STAT2));
|
||||
|
||||
priv->viu.osd1_enabled = false;
|
||||
priv->viu.osd1_commit = false;
|
||||
priv->viu.osd1_interlace = false;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Video Input Unit */
|
||||
|
||||
#ifndef __MESON_VIU_H
|
||||
#define __MESON_VIU_H
|
||||
|
||||
/* OSDx_BLKx_CFG */
|
||||
#define OSD_CANVAS_SEL 16
|
||||
|
||||
#define OSD_ENDIANNESS_LE BIT(15)
|
||||
#define OSD_ENDIANNESS_BE (0)
|
||||
|
||||
#define OSD_BLK_MODE_422 (0x03 << 8)
|
||||
#define OSD_BLK_MODE_16 (0x04 << 8)
|
||||
#define OSD_BLK_MODE_32 (0x05 << 8)
|
||||
#define OSD_BLK_MODE_24 (0x07 << 8)
|
||||
|
||||
#define OSD_OUTPUT_COLOR_RGB BIT(7)
|
||||
#define OSD_OUTPUT_COLOR_YUV (0)
|
||||
|
||||
#define OSD_COLOR_MATRIX_32_RGBA (0x00 << 2)
|
||||
#define OSD_COLOR_MATRIX_32_ARGB (0x01 << 2)
|
||||
#define OSD_COLOR_MATRIX_32_ABGR (0x02 << 2)
|
||||
#define OSD_COLOR_MATRIX_32_BGRA (0x03 << 2)
|
||||
|
||||
#define OSD_COLOR_MATRIX_24_RGB (0x00 << 2)
|
||||
|
||||
#define OSD_COLOR_MATRIX_16_RGB655 (0x00 << 2)
|
||||
#define OSD_COLOR_MATRIX_16_RGB565 (0x04 << 2)
|
||||
|
||||
#define OSD_INTERLACE_ENABLED BIT(1)
|
||||
#define OSD_INTERLACE_ODD BIT(0)
|
||||
#define OSD_INTERLACE_EVEN (0)
|
||||
|
||||
/* OSDx_CTRL_STAT */
|
||||
#define OSD_ENABLE BIT(21)
|
||||
#define OSD_BLK0_ENABLE BIT(0)
|
||||
|
||||
#define OSD_GLOBAL_ALPHA_SHIFT 12
|
||||
|
||||
/* OSDx_CTRL_STAT2 */
|
||||
#define OSD_REPLACE_EN BIT(14)
|
||||
#define OSD_REPLACE_SHIFT 6
|
||||
|
||||
void meson_viu_init(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_VIU_H */
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "meson_drv.h"
|
||||
#include "meson_vpp.h"
|
||||
#include "meson_registers.h"
|
||||
|
||||
/*
|
||||
* VPP Handles all the Post Processing after the Scanout from the VIU
|
||||
* We handle the following post processings :
|
||||
* - Postblend : Blends the OSD1 only
|
||||
* We exclude OSD2, VS1, VS1 and Preblend output
|
||||
* - Vertical OSD Scaler for OSD1 only, we disable vertical scaler and
|
||||
* use it only for interlace scanout
|
||||
* - Intermediate FIFO with default Amlogic values
|
||||
*
|
||||
* What is missing :
|
||||
* - Preblend for video overlay pre-scaling
|
||||
* - OSD2 support for cursor framebuffer
|
||||
* - Video pre-scaling before postblend
|
||||
* - Full Vertical/Horizontal OSD scaling to support TV overscan
|
||||
* - HDR conversion
|
||||
*/
|
||||
|
||||
void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux)
|
||||
{
|
||||
writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL));
|
||||
}
|
||||
|
||||
/*
|
||||
* When the output is interlaced, the OSD must switch between
|
||||
* each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0
|
||||
* at each vsync.
|
||||
* But the vertical scaler can provide such funtionnality if
|
||||
* is configured for 2:1 scaling with interlace options enabled.
|
||||
*/
|
||||
void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv,
|
||||
struct drm_rect *input)
|
||||
{
|
||||
writel_relaxed(BIT(3) /* Enable scaler */ |
|
||||
BIT(2), /* Select OSD1 */
|
||||
priv->io_base + _REG(VPP_OSD_SC_CTRL0));
|
||||
|
||||
writel_relaxed(((drm_rect_width(input) - 1) << 16) |
|
||||
(drm_rect_height(input) - 1),
|
||||
priv->io_base + _REG(VPP_OSD_SCI_WH_M1));
|
||||
/* 2:1 scaling */
|
||||
writel_relaxed(((input->x1) << 16) | (input->x2),
|
||||
priv->io_base + _REG(VPP_OSD_SCO_H_START_END));
|
||||
writel_relaxed(((input->y1 >> 1) << 16) | (input->y2 >> 1),
|
||||
priv->io_base + _REG(VPP_OSD_SCO_V_START_END));
|
||||
|
||||
/* 2:1 scaling values */
|
||||
writel_relaxed(BIT(16), priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE));
|
||||
writel_relaxed(BIT(25), priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP));
|
||||
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
|
||||
|
||||
writel_relaxed((4 << 0) /* osd_vsc_bank_length */ |
|
||||
(4 << 3) /* osd_vsc_top_ini_rcv_num0 */ |
|
||||
(1 << 8) /* osd_vsc_top_rpt_p0_num0 */ |
|
||||
(6 << 11) /* osd_vsc_bot_ini_rcv_num0 */ |
|
||||
(2 << 16) /* osd_vsc_bot_rpt_p0_num0 */ |
|
||||
BIT(23) /* osd_prog_interlace */ |
|
||||
BIT(24), /* Enable vertical scaler */
|
||||
priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
|
||||
}
|
||||
|
||||
void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv)
|
||||
{
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
|
||||
}
|
||||
|
||||
static unsigned int vpp_filter_coefs_4point_bspline[] = {
|
||||
0x15561500, 0x14561600, 0x13561700, 0x12561800,
|
||||
0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00,
|
||||
0x0f531e00, 0x0e531f00, 0x0d522100, 0x0c522200,
|
||||
0x0b522300, 0x0b512400, 0x0a502600, 0x0a4f2700,
|
||||
0x094e2900, 0x084e2a00, 0x084d2b00, 0x074c2c01,
|
||||
0x074b2d01, 0x064a2f01, 0x06493001, 0x05483201,
|
||||
0x05473301, 0x05463401, 0x04453601, 0x04433702,
|
||||
0x04423802, 0x03413a02, 0x03403b02, 0x033f3c02,
|
||||
0x033d3d03
|
||||
};
|
||||
|
||||
static void meson_vpp_write_scaling_filter_coefs(struct meson_drm *priv,
|
||||
const unsigned int *coefs,
|
||||
bool is_horizontal)
|
||||
{
|
||||
int i;
|
||||
|
||||
writel_relaxed(is_horizontal ? BIT(8) : 0,
|
||||
priv->io_base + _REG(VPP_OSD_SCALE_COEF_IDX));
|
||||
for (i = 0; i < 33; i++)
|
||||
writel_relaxed(coefs[i],
|
||||
priv->io_base + _REG(VPP_OSD_SCALE_COEF));
|
||||
}
|
||||
|
||||
void meson_vpp_init(struct meson_drm *priv)
|
||||
{
|
||||
/* set dummy data default YUV black */
|
||||
if (meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu"))
|
||||
writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1));
|
||||
else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu")) {
|
||||
writel_bits_relaxed(0xff << 16, 0xff << 16,
|
||||
priv->io_base + _REG(VIU_MISC_CTRL1));
|
||||
writel_relaxed(0x20000, priv->io_base + _REG(VPP_DOLBY_CTRL));
|
||||
writel_relaxed(0x1020080,
|
||||
priv->io_base + _REG(VPP_DUMMY_DATA1));
|
||||
}
|
||||
|
||||
/* Initialize vpu fifo control registers */
|
||||
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));
|
||||
|
||||
/* Turn off preblend */
|
||||
writel_bits_relaxed(VPP_PREBLEND_ENABLE, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
/* Turn off POSTBLEND */
|
||||
writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
/* Force all planes off */
|
||||
writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND |
|
||||
VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND, 0,
|
||||
priv->io_base + _REG(VPP_MISC));
|
||||
|
||||
/* Disable Scalers */
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0));
|
||||
writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0));
|
||||
|
||||
/* Write in the proper filter coefficients. */
|
||||
meson_vpp_write_scaling_filter_coefs(priv,
|
||||
vpp_filter_coefs_4point_bspline, false);
|
||||
meson_vpp_write_scaling_filter_coefs(priv,
|
||||
vpp_filter_coefs_4point_bspline, true);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Video Post Process */
|
||||
|
||||
#ifndef __MESON_VPP_H
|
||||
#define __MESON_VPP_H
|
||||
|
||||
/* Mux VIU/VPP to ENCI */
|
||||
#define MESON_VIU_VPP_MUX_ENCI 0x5
|
||||
|
||||
void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux);
|
||||
|
||||
void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv,
|
||||
struct drm_rect *input);
|
||||
void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv);
|
||||
|
||||
void meson_vpp_init(struct meson_drm *priv);
|
||||
|
||||
#endif /* __MESON_VPP_H */
|
Loading…
Reference in New Issue