You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user