mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 11:18:45 +07:00
9f68e3655a
uapi: - dma-buf heaps added (and fixed) - command line add support for panel oreientation - command line allow overriding penguin count drm: - mipi dsi definition updates - lockdep annotations for dma_resv - remove dma-buf kmap/kunmap support - constify fb_ops in all fbdev drivers - MST fix for daisy chained hotplug- - CTA-861-G modes with VIC >= 193 added - fix drm_panel_of_backlight export - LVDS decoder support - more device based logging support - scanline alighment for dumb buffers - MST DSC helpers scheduler: - documentation fixes - job distribution improvements panel: - Logic PD type 28 panel support - Jimax8729d MIPI-DSI - igenic JZ4770 - generic DSI devicetree bindings - sony acx424AKP panel - Leadtek LTK500HD1829 - xinpeng XPP055C272 - AUO B116XAK01 - GiantPlus GPM940B0 - BOE NV140FHM-N49 - Satoz SAT050AT40H12R2 - Sharp LS020B1DD01D panels. ttm: - use blocking WW lock i915: - hw/uapi state separation - Lock annotation improvements - selftest improvements - ICL/TGL DSI VDSC support - VBT parsing improvments - Display refactoring - DSI updates + fixes - HDCP 2.2 for CFL - CML PCI ID fixes - GLK+ fbc fix - PSR fixes - GEN/GT refactor improvments - DP MST fixes - switch context id alloc to xarray - workaround updates - LMEM debugfs support - tiled monitor fixes - ICL+ clock gating programming removed - DP MST disable sequence fixed - LMEM discontiguous object maps - prefaulting for discontiguous objects - use LMEM for dumb buffers if possible - add LMEM mmap support amdgpu: - enable sync object timelines for vulkan - MST atomic routines - enable MST DSC support - add DMCUB display microengine support - DC OEM i2c support - Renoir DC fixes - Initial HDCP 2.x support - BACO support for Arcturus - Use BACO for runtime PM power save - gfxoff on navi10 - gfx10 golden updates and fixes - DCN support on POWER - GFXOFF for raven1 refresh - MM engine idle handlers cleanup - 10bpc EDP panel fixes - renoir watermark fixes - SR-IOV fixes - Arcturus VCN fixes - GDDR6 training fixes - freesync fixes - Pollock support amdkfd: - unify more codepath with amdgpu - use KIQ to setup HIQ rather than MMIO radeon: - fix vma fault handler race - PPC DMA fix - register check fixes for r100/r200 nouveau: - mmap_sem vs dma_resv fix - rewrite the ACR secure boot code for Turing - TU10x graphics engine support (TU11x pending) - Page kind mapping for turing - 10-bit LUT support - GP10B Tegra fixes - HD audio regression fix hisilicon/hibmc: - use generic fbdev code and helpers rockchip: - dsi/px30 support virtio: - fb damage support - static some functions vc4: - use dma_resv lock wrappers msm: - use dma_resv lock wrappers - sc7180 display + DSI support - a618 support - UBWC support improvements vmwgfx: - updates + new logging uapi exynos: - enable/disable callback cleanups etnaviv: - use dma_resv lock wrappers atmel-hlcdc: - clock fixes mediatek: - cmdq support - non-smooth cursor fixes - ctm property support sun4i: - suspend support - A64 mipi dsi support rcar-du: - Color management module support - LVDS encoder dual-link support - R8A77980 support analogic: - add support for an6345 ast: - atomic modeset support - primary plane garbage fix arcgpu: - fixes for fourcc handling tegra: - minor fixes and improvments mcde: - vblank support meson: - OSD1 plane AFBC commit gma500: - add pageflip support - reomve global drm_dev komeda: - tweak debugfs output - d32 support - runtime PM suppotr udl: - use generic shmem helpers - cleanup and fixes -----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJeMm6RAAoJEAx081l5xIa+vN8P/0j4jEOv+KIinAhoH+LG3EpD m2TUuu5OQIoBrcCoWOgFBk3wqYpw6PdMBdkXh+5sE5lfeBynp8oC3Bin+QsHJE05 eGBpZtHe+70MQb0Eha+Aic0hchvBKzRnq6i0MYSIHn6afs76dLmF8knTjycxrvV5 Xu1Z3WDmjzqgWF9ja5JCD6fby11seP5RrwObYKVikO35QQyJJwGSGKgu5rq/pByK /n0PCnCOINuL0Lz6J9qexdh/0/XYFQilRC31GJNlKbDSFuECF0GOEzEE/xUBW/pI dLh2YwIIygm18Gar9PgvMwXJn3BfzQ0qEJsf+HlQeNw9iLgbHpp2AsTxHTE87OGe R/y85taW3jGjPsNOKZOeLpvg/Ro8l8ZipLApvDCG2O22DThg/cd6NDjZxl1FJfRH acDG/JdgPo5MbdRAH/cM1WuFS9gEM+0BeSQ5gCjtPakF+X4Vz+ABFDLMRJoaejkJ q8DG32TQXELQx0RMghsqK7YCWGfl+2alA1u9w6TgJh9Rq4iVckvpDeqAZnK1Adkc 87g957Tl0n6FA4wJj/t5jrceiLRMJAm/rBK+R3GZNfWrgx4bHbCmb4fZDZsrFzph nbAjNJ5kOchrFCaRR47ULby6+Q14MAFbkWq4Crfu4YDdzUkTPpep6pi2GIe8w0rV P0hdYOYJf6LUda0utuQX =oFrI -----END PGP SIGNATURE----- Merge tag 'drm-next-2020-01-30' of git://anongit.freedesktop.org/drm/drm Pull drm updates from Davbe Airlie: "This is the main pull request for graphics for 5.6. Usual selection of changes all over. I've got one outstanding vmwgfx pull that touches mm so kept it separate until after all of this lands. I'll try and get it to you soon after this, but it might be early next week (nothing wrong with code, just my schedule is messy) This also hits a lot of fbdev drivers with some cleanups. Other notables: - vulkan timeline semaphore support added to syncobjs - nouveau turing secureboot/graphics support - Displayport MST display stream compression support Detailed summary: uapi: - dma-buf heaps added (and fixed) - command line add support for panel oreientation - command line allow overriding penguin count drm: - mipi dsi definition updates - lockdep annotations for dma_resv - remove dma-buf kmap/kunmap support - constify fb_ops in all fbdev drivers - MST fix for daisy chained hotplug- - CTA-861-G modes with VIC >= 193 added - fix drm_panel_of_backlight export - LVDS decoder support - more device based logging support - scanline alighment for dumb buffers - MST DSC helpers scheduler: - documentation fixes - job distribution improvements panel: - Logic PD type 28 panel support - Jimax8729d MIPI-DSI - igenic JZ4770 - generic DSI devicetree bindings - sony acx424AKP panel - Leadtek LTK500HD1829 - xinpeng XPP055C272 - AUO B116XAK01 - GiantPlus GPM940B0 - BOE NV140FHM-N49 - Satoz SAT050AT40H12R2 - Sharp LS020B1DD01D panels. ttm: - use blocking WW lock i915: - hw/uapi state separation - Lock annotation improvements - selftest improvements - ICL/TGL DSI VDSC support - VBT parsing improvments - Display refactoring - DSI updates + fixes - HDCP 2.2 for CFL - CML PCI ID fixes - GLK+ fbc fix - PSR fixes - GEN/GT refactor improvments - DP MST fixes - switch context id alloc to xarray - workaround updates - LMEM debugfs support - tiled monitor fixes - ICL+ clock gating programming removed - DP MST disable sequence fixed - LMEM discontiguous object maps - prefaulting for discontiguous objects - use LMEM for dumb buffers if possible - add LMEM mmap support amdgpu: - enable sync object timelines for vulkan - MST atomic routines - enable MST DSC support - add DMCUB display microengine support - DC OEM i2c support - Renoir DC fixes - Initial HDCP 2.x support - BACO support for Arcturus - Use BACO for runtime PM power save - gfxoff on navi10 - gfx10 golden updates and fixes - DCN support on POWER - GFXOFF for raven1 refresh - MM engine idle handlers cleanup - 10bpc EDP panel fixes - renoir watermark fixes - SR-IOV fixes - Arcturus VCN fixes - GDDR6 training fixes - freesync fixes - Pollock support amdkfd: - unify more codepath with amdgpu - use KIQ to setup HIQ rather than MMIO radeon: - fix vma fault handler race - PPC DMA fix - register check fixes for r100/r200 nouveau: - mmap_sem vs dma_resv fix - rewrite the ACR secure boot code for Turing - TU10x graphics engine support (TU11x pending) - Page kind mapping for turing - 10-bit LUT support - GP10B Tegra fixes - HD audio regression fix hisilicon/hibmc: - use generic fbdev code and helpers rockchip: - dsi/px30 support virtio: - fb damage support - static some functions vc4: - use dma_resv lock wrappers msm: - use dma_resv lock wrappers - sc7180 display + DSI support - a618 support - UBWC support improvements vmwgfx: - updates + new logging uapi exynos: - enable/disable callback cleanups etnaviv: - use dma_resv lock wrappers atmel-hlcdc: - clock fixes mediatek: - cmdq support - non-smooth cursor fixes - ctm property support sun4i: - suspend support - A64 mipi dsi support rcar-du: - Color management module support - LVDS encoder dual-link support - R8A77980 support analogic: - add support for an6345 ast: - atomic modeset support - primary plane garbage fix arcgpu: - fixes for fourcc handling tegra: - minor fixes and improvments mcde: - vblank support meson: - OSD1 plane AFBC commit gma500: - add pageflip support - reomve global drm_dev komeda: - tweak debugfs output - d32 support - runtime PM suppotr udl: - use generic shmem helpers - cleanup and fixes" * tag 'drm-next-2020-01-30' of git://anongit.freedesktop.org/drm/drm: (1998 commits) drm/nouveau/fb/gp102-: allow module to load even when scrubber binary is missing drm/nouveau/acr: return error when registering LSF if ACR not supported drm/nouveau/disp/gv100-: not all channel types support reporting error codes drm/nouveau/disp/nv50-: prevent oops when no channel method map provided drm/nouveau: support synchronous pushbuf submission drm/nouveau: signal pending fences when channel has been killed drm/nouveau: reject attempts to submit to dead channels drm/nouveau: zero vma pointer even if we only unreference it rather than free drm/nouveau: Add HD-audio component notifier support drm/nouveau: fix build error without CONFIG_IOMMU_API drm/nouveau/kms/nv04: remove set but not used variable 'width' drm/nouveau/kms/nv50: remove set but not unused variable 'nv_connector' drm/nouveau/mmu: fix comptag memory leak drm/nouveau/gr/gp10b: Use gp100_grctx and gp100_gr_zbc drm/nouveau/pmu/gm20b,gp10b: Fix Falcon bootstrapping drm/exynos: Rename Exynos to lowercase drm/exynos: change callback names drm/mst: Don't do atomic checks over disabled managers drm/amdgpu: add the lost mutex_init back drm/amd/display: skip opp blank or unblank if test pattern enabled ...
601 lines
15 KiB
C
601 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) STMicroelectronics SA 2014
|
|
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
|
|
*/
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/component.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_bridge.h>
|
|
#include <drm/drm_device.h>
|
|
#include <drm/drm_panel.h>
|
|
#include <drm/drm_print.h>
|
|
#include <drm/drm_probe_helper.h>
|
|
|
|
#include "sti_awg_utils.h"
|
|
#include "sti_drv.h"
|
|
#include "sti_mixer.h"
|
|
|
|
/* DVO registers */
|
|
#define DVO_AWG_DIGSYNC_CTRL 0x0000
|
|
#define DVO_DOF_CFG 0x0004
|
|
#define DVO_LUT_PROG_LOW 0x0008
|
|
#define DVO_LUT_PROG_MID 0x000C
|
|
#define DVO_LUT_PROG_HIGH 0x0010
|
|
#define DVO_DIGSYNC_INSTR_I 0x0100
|
|
|
|
#define DVO_AWG_CTRL_EN BIT(0)
|
|
#define DVO_AWG_FRAME_BASED_SYNC BIT(2)
|
|
|
|
#define DVO_DOF_EN_LOWBYTE BIT(0)
|
|
#define DVO_DOF_EN_MIDBYTE BIT(1)
|
|
#define DVO_DOF_EN_HIGHBYTE BIT(2)
|
|
#define DVO_DOF_EN BIT(6)
|
|
#define DVO_DOF_MOD_COUNT_SHIFT 8
|
|
|
|
#define DVO_LUT_ZERO 0
|
|
#define DVO_LUT_Y_G 1
|
|
#define DVO_LUT_Y_G_DEL 2
|
|
#define DVO_LUT_CB_B 3
|
|
#define DVO_LUT_CB_B_DEL 4
|
|
#define DVO_LUT_CR_R 5
|
|
#define DVO_LUT_CR_R_DEL 6
|
|
#define DVO_LUT_HOLD 7
|
|
|
|
struct dvo_config {
|
|
u32 flags;
|
|
u32 lowbyte;
|
|
u32 midbyte;
|
|
u32 highbyte;
|
|
int (*awg_fwgen_fct)(
|
|
struct awg_code_generation_params *fw_gen_params,
|
|
struct awg_timing *timing);
|
|
};
|
|
|
|
static struct dvo_config rgb_24bit_de_cfg = {
|
|
.flags = (0L << DVO_DOF_MOD_COUNT_SHIFT),
|
|
.lowbyte = DVO_LUT_CR_R,
|
|
.midbyte = DVO_LUT_Y_G,
|
|
.highbyte = DVO_LUT_CB_B,
|
|
.awg_fwgen_fct = sti_awg_generate_code_data_enable_mode,
|
|
};
|
|
|
|
/*
|
|
* STI digital video output structure
|
|
*
|
|
* @dev: driver device
|
|
* @drm_dev: pointer to drm device
|
|
* @mode: current display mode selected
|
|
* @regs: dvo registers
|
|
* @clk_pix: pixel clock for dvo
|
|
* @clk: clock for dvo
|
|
* @clk_main_parent: dvo parent clock if main path used
|
|
* @clk_aux_parent: dvo parent clock if aux path used
|
|
* @panel_node: panel node reference from device tree
|
|
* @panel: reference to the panel connected to the dvo
|
|
* @enabled: true if dvo is enabled else false
|
|
* @encoder: drm_encoder it is bound
|
|
*/
|
|
struct sti_dvo {
|
|
struct device dev;
|
|
struct drm_device *drm_dev;
|
|
struct drm_display_mode mode;
|
|
void __iomem *regs;
|
|
struct clk *clk_pix;
|
|
struct clk *clk;
|
|
struct clk *clk_main_parent;
|
|
struct clk *clk_aux_parent;
|
|
struct device_node *panel_node;
|
|
struct drm_panel *panel;
|
|
struct dvo_config *config;
|
|
bool enabled;
|
|
struct drm_encoder *encoder;
|
|
struct drm_bridge *bridge;
|
|
};
|
|
|
|
struct sti_dvo_connector {
|
|
struct drm_connector drm_connector;
|
|
struct drm_encoder *encoder;
|
|
struct sti_dvo *dvo;
|
|
};
|
|
|
|
#define to_sti_dvo_connector(x) \
|
|
container_of(x, struct sti_dvo_connector, drm_connector)
|
|
|
|
#define BLANKING_LEVEL 16
|
|
static int dvo_awg_generate_code(struct sti_dvo *dvo, u8 *ram_size, u32 *ram_code)
|
|
{
|
|
struct drm_display_mode *mode = &dvo->mode;
|
|
struct dvo_config *config = dvo->config;
|
|
struct awg_code_generation_params fw_gen_params;
|
|
struct awg_timing timing;
|
|
|
|
fw_gen_params.ram_code = ram_code;
|
|
fw_gen_params.instruction_offset = 0;
|
|
|
|
timing.total_lines = mode->vtotal;
|
|
timing.active_lines = mode->vdisplay;
|
|
timing.blanking_lines = mode->vsync_start - mode->vdisplay;
|
|
timing.trailing_lines = mode->vtotal - mode->vsync_start;
|
|
timing.total_pixels = mode->htotal;
|
|
timing.active_pixels = mode->hdisplay;
|
|
timing.blanking_pixels = mode->hsync_start - mode->hdisplay;
|
|
timing.trailing_pixels = mode->htotal - mode->hsync_start;
|
|
timing.blanking_level = BLANKING_LEVEL;
|
|
|
|
if (config->awg_fwgen_fct(&fw_gen_params, &timing)) {
|
|
DRM_ERROR("AWG firmware not properly generated\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
*ram_size = fw_gen_params.instruction_offset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Configure AWG, writing instructions
|
|
*
|
|
* @dvo: pointer to DVO structure
|
|
* @awg_ram_code: pointer to AWG instructions table
|
|
* @nb: nb of AWG instructions
|
|
*/
|
|
static void dvo_awg_configure(struct sti_dvo *dvo, u32 *awg_ram_code, int nb)
|
|
{
|
|
int i;
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
for (i = 0; i < nb; i++)
|
|
writel(awg_ram_code[i],
|
|
dvo->regs + DVO_DIGSYNC_INSTR_I + i * 4);
|
|
for (i = nb; i < AWG_MAX_INST; i++)
|
|
writel(0, dvo->regs + DVO_DIGSYNC_INSTR_I + i * 4);
|
|
|
|
writel(DVO_AWG_CTRL_EN, dvo->regs + DVO_AWG_DIGSYNC_CTRL);
|
|
}
|
|
|
|
#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \
|
|
readl(dvo->regs + reg))
|
|
|
|
static void dvo_dbg_awg_microcode(struct seq_file *s, void __iomem *reg)
|
|
{
|
|
unsigned int i;
|
|
|
|
seq_puts(s, "\n\n");
|
|
seq_puts(s, " DVO AWG microcode:");
|
|
for (i = 0; i < AWG_MAX_INST; i++) {
|
|
if (i % 8 == 0)
|
|
seq_printf(s, "\n %04X:", i);
|
|
seq_printf(s, " %04X", readl(reg + i * 4));
|
|
}
|
|
}
|
|
|
|
static int dvo_dbg_show(struct seq_file *s, void *data)
|
|
{
|
|
struct drm_info_node *node = s->private;
|
|
struct sti_dvo *dvo = (struct sti_dvo *)node->info_ent->data;
|
|
|
|
seq_printf(s, "DVO: (vaddr = 0x%p)", dvo->regs);
|
|
DBGFS_DUMP(DVO_AWG_DIGSYNC_CTRL);
|
|
DBGFS_DUMP(DVO_DOF_CFG);
|
|
DBGFS_DUMP(DVO_LUT_PROG_LOW);
|
|
DBGFS_DUMP(DVO_LUT_PROG_MID);
|
|
DBGFS_DUMP(DVO_LUT_PROG_HIGH);
|
|
dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I);
|
|
seq_putc(s, '\n');
|
|
return 0;
|
|
}
|
|
|
|
static struct drm_info_list dvo_debugfs_files[] = {
|
|
{ "dvo", dvo_dbg_show, 0, NULL },
|
|
};
|
|
|
|
static int dvo_debugfs_init(struct sti_dvo *dvo, struct drm_minor *minor)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dvo_debugfs_files); i++)
|
|
dvo_debugfs_files[i].data = dvo;
|
|
|
|
return drm_debugfs_create_files(dvo_debugfs_files,
|
|
ARRAY_SIZE(dvo_debugfs_files),
|
|
minor->debugfs_root, minor);
|
|
}
|
|
|
|
static void sti_dvo_disable(struct drm_bridge *bridge)
|
|
{
|
|
struct sti_dvo *dvo = bridge->driver_private;
|
|
|
|
if (!dvo->enabled)
|
|
return;
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
if (dvo->config->awg_fwgen_fct)
|
|
writel(0x00000000, dvo->regs + DVO_AWG_DIGSYNC_CTRL);
|
|
|
|
writel(0x00000000, dvo->regs + DVO_DOF_CFG);
|
|
|
|
drm_panel_disable(dvo->panel);
|
|
|
|
/* Disable/unprepare dvo clock */
|
|
clk_disable_unprepare(dvo->clk_pix);
|
|
clk_disable_unprepare(dvo->clk);
|
|
|
|
dvo->enabled = false;
|
|
}
|
|
|
|
static void sti_dvo_pre_enable(struct drm_bridge *bridge)
|
|
{
|
|
struct sti_dvo *dvo = bridge->driver_private;
|
|
struct dvo_config *config = dvo->config;
|
|
u32 val;
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
if (dvo->enabled)
|
|
return;
|
|
|
|
/* Make sure DVO is disabled */
|
|
writel(0x00000000, dvo->regs + DVO_DOF_CFG);
|
|
writel(0x00000000, dvo->regs + DVO_AWG_DIGSYNC_CTRL);
|
|
|
|
if (config->awg_fwgen_fct) {
|
|
u8 nb_instr;
|
|
u32 awg_ram_code[AWG_MAX_INST];
|
|
/* Configure AWG */
|
|
if (!dvo_awg_generate_code(dvo, &nb_instr, awg_ram_code))
|
|
dvo_awg_configure(dvo, awg_ram_code, nb_instr);
|
|
else
|
|
return;
|
|
}
|
|
|
|
/* Prepare/enable clocks */
|
|
if (clk_prepare_enable(dvo->clk_pix))
|
|
DRM_ERROR("Failed to prepare/enable dvo_pix clk\n");
|
|
if (clk_prepare_enable(dvo->clk))
|
|
DRM_ERROR("Failed to prepare/enable dvo clk\n");
|
|
|
|
drm_panel_enable(dvo->panel);
|
|
|
|
/* Set LUT */
|
|
writel(config->lowbyte, dvo->regs + DVO_LUT_PROG_LOW);
|
|
writel(config->midbyte, dvo->regs + DVO_LUT_PROG_MID);
|
|
writel(config->highbyte, dvo->regs + DVO_LUT_PROG_HIGH);
|
|
|
|
/* Digital output formatter config */
|
|
val = (config->flags | DVO_DOF_EN);
|
|
writel(val, dvo->regs + DVO_DOF_CFG);
|
|
|
|
dvo->enabled = true;
|
|
}
|
|
|
|
static void sti_dvo_set_mode(struct drm_bridge *bridge,
|
|
const struct drm_display_mode *mode,
|
|
const struct drm_display_mode *adjusted_mode)
|
|
{
|
|
struct sti_dvo *dvo = bridge->driver_private;
|
|
struct sti_mixer *mixer = to_sti_mixer(dvo->encoder->crtc);
|
|
int rate = mode->clock * 1000;
|
|
struct clk *clkp;
|
|
int ret;
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
memcpy(&dvo->mode, mode, sizeof(struct drm_display_mode));
|
|
|
|
/* According to the path used (main or aux), the dvo clocks should
|
|
* have a different parent clock. */
|
|
if (mixer->id == STI_MIXER_MAIN)
|
|
clkp = dvo->clk_main_parent;
|
|
else
|
|
clkp = dvo->clk_aux_parent;
|
|
|
|
if (clkp) {
|
|
clk_set_parent(dvo->clk_pix, clkp);
|
|
clk_set_parent(dvo->clk, clkp);
|
|
}
|
|
|
|
/* DVO clocks = compositor clock */
|
|
ret = clk_set_rate(dvo->clk_pix, rate);
|
|
if (ret < 0) {
|
|
DRM_ERROR("Cannot set rate (%dHz) for dvo_pix clk\n", rate);
|
|
return;
|
|
}
|
|
|
|
ret = clk_set_rate(dvo->clk, rate);
|
|
if (ret < 0) {
|
|
DRM_ERROR("Cannot set rate (%dHz) for dvo clk\n", rate);
|
|
return;
|
|
}
|
|
|
|
/* For now, we only support 24bit data enable (DE) synchro format */
|
|
dvo->config = &rgb_24bit_de_cfg;
|
|
}
|
|
|
|
static void sti_dvo_bridge_nope(struct drm_bridge *bridge)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
|
|
static const struct drm_bridge_funcs sti_dvo_bridge_funcs = {
|
|
.pre_enable = sti_dvo_pre_enable,
|
|
.enable = sti_dvo_bridge_nope,
|
|
.disable = sti_dvo_disable,
|
|
.post_disable = sti_dvo_bridge_nope,
|
|
.mode_set = sti_dvo_set_mode,
|
|
};
|
|
|
|
static int sti_dvo_connector_get_modes(struct drm_connector *connector)
|
|
{
|
|
struct sti_dvo_connector *dvo_connector
|
|
= to_sti_dvo_connector(connector);
|
|
struct sti_dvo *dvo = dvo_connector->dvo;
|
|
|
|
if (dvo->panel)
|
|
return drm_panel_get_modes(dvo->panel, connector);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define CLK_TOLERANCE_HZ 50
|
|
|
|
static int sti_dvo_connector_mode_valid(struct drm_connector *connector,
|
|
struct drm_display_mode *mode)
|
|
{
|
|
int target = mode->clock * 1000;
|
|
int target_min = target - CLK_TOLERANCE_HZ;
|
|
int target_max = target + CLK_TOLERANCE_HZ;
|
|
int result;
|
|
struct sti_dvo_connector *dvo_connector
|
|
= to_sti_dvo_connector(connector);
|
|
struct sti_dvo *dvo = dvo_connector->dvo;
|
|
|
|
result = clk_round_rate(dvo->clk_pix, target);
|
|
|
|
DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n",
|
|
target, result);
|
|
|
|
if ((result < target_min) || (result > target_max)) {
|
|
DRM_DEBUG_DRIVER("dvo pixclk=%d not supported\n", target);
|
|
return MODE_BAD;
|
|
}
|
|
|
|
return MODE_OK;
|
|
}
|
|
|
|
static const
|
|
struct drm_connector_helper_funcs sti_dvo_connector_helper_funcs = {
|
|
.get_modes = sti_dvo_connector_get_modes,
|
|
.mode_valid = sti_dvo_connector_mode_valid,
|
|
};
|
|
|
|
static enum drm_connector_status
|
|
sti_dvo_connector_detect(struct drm_connector *connector, bool force)
|
|
{
|
|
struct sti_dvo_connector *dvo_connector
|
|
= to_sti_dvo_connector(connector);
|
|
struct sti_dvo *dvo = dvo_connector->dvo;
|
|
|
|
DRM_DEBUG_DRIVER("\n");
|
|
|
|
if (!dvo->panel) {
|
|
dvo->panel = of_drm_find_panel(dvo->panel_node);
|
|
if (IS_ERR(dvo->panel))
|
|
dvo->panel = NULL;
|
|
else
|
|
drm_panel_attach(dvo->panel, connector);
|
|
}
|
|
|
|
if (dvo->panel)
|
|
return connector_status_connected;
|
|
|
|
return connector_status_disconnected;
|
|
}
|
|
|
|
static int sti_dvo_late_register(struct drm_connector *connector)
|
|
{
|
|
struct sti_dvo_connector *dvo_connector
|
|
= to_sti_dvo_connector(connector);
|
|
struct sti_dvo *dvo = dvo_connector->dvo;
|
|
|
|
if (dvo_debugfs_init(dvo, dvo->drm_dev->primary)) {
|
|
DRM_ERROR("DVO debugfs setup failed\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct drm_connector_funcs sti_dvo_connector_funcs = {
|
|
.fill_modes = drm_helper_probe_single_connector_modes,
|
|
.detect = sti_dvo_connector_detect,
|
|
.destroy = drm_connector_cleanup,
|
|
.reset = drm_atomic_helper_connector_reset,
|
|
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
|
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
|
.late_register = sti_dvo_late_register,
|
|
};
|
|
|
|
static struct drm_encoder *sti_dvo_find_encoder(struct drm_device *dev)
|
|
{
|
|
struct drm_encoder *encoder;
|
|
|
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
|
if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS)
|
|
return encoder;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int sti_dvo_bind(struct device *dev, struct device *master, void *data)
|
|
{
|
|
struct sti_dvo *dvo = dev_get_drvdata(dev);
|
|
struct drm_device *drm_dev = data;
|
|
struct drm_encoder *encoder;
|
|
struct sti_dvo_connector *connector;
|
|
struct drm_connector *drm_connector;
|
|
struct drm_bridge *bridge;
|
|
int err;
|
|
|
|
/* Set the drm device handle */
|
|
dvo->drm_dev = drm_dev;
|
|
|
|
encoder = sti_dvo_find_encoder(drm_dev);
|
|
if (!encoder)
|
|
return -ENOMEM;
|
|
|
|
connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
|
|
if (!connector)
|
|
return -ENOMEM;
|
|
|
|
connector->dvo = dvo;
|
|
|
|
bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
|
|
if (!bridge)
|
|
return -ENOMEM;
|
|
|
|
bridge->driver_private = dvo;
|
|
bridge->funcs = &sti_dvo_bridge_funcs;
|
|
bridge->of_node = dvo->dev.of_node;
|
|
drm_bridge_add(bridge);
|
|
|
|
err = drm_bridge_attach(encoder, bridge, NULL);
|
|
if (err) {
|
|
DRM_ERROR("Failed to attach bridge\n");
|
|
return err;
|
|
}
|
|
|
|
dvo->bridge = bridge;
|
|
connector->encoder = encoder;
|
|
dvo->encoder = encoder;
|
|
|
|
drm_connector = (struct drm_connector *)connector;
|
|
|
|
drm_connector->polled = DRM_CONNECTOR_POLL_HPD;
|
|
|
|
drm_connector_init(drm_dev, drm_connector,
|
|
&sti_dvo_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
|
|
drm_connector_helper_add(drm_connector,
|
|
&sti_dvo_connector_helper_funcs);
|
|
|
|
err = drm_connector_attach_encoder(drm_connector, encoder);
|
|
if (err) {
|
|
DRM_ERROR("Failed to attach a connector to a encoder\n");
|
|
goto err_sysfs;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_sysfs:
|
|
drm_bridge_remove(bridge);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void sti_dvo_unbind(struct device *dev,
|
|
struct device *master, void *data)
|
|
{
|
|
struct sti_dvo *dvo = dev_get_drvdata(dev);
|
|
|
|
drm_bridge_remove(dvo->bridge);
|
|
}
|
|
|
|
static const struct component_ops sti_dvo_ops = {
|
|
.bind = sti_dvo_bind,
|
|
.unbind = sti_dvo_unbind,
|
|
};
|
|
|
|
static int sti_dvo_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct sti_dvo *dvo;
|
|
struct resource *res;
|
|
struct device_node *np = dev->of_node;
|
|
|
|
DRM_INFO("%s\n", __func__);
|
|
|
|
dvo = devm_kzalloc(dev, sizeof(*dvo), GFP_KERNEL);
|
|
if (!dvo) {
|
|
DRM_ERROR("Failed to allocate memory for DVO\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
dvo->dev = pdev->dev;
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dvo-reg");
|
|
if (!res) {
|
|
DRM_ERROR("Invalid dvo resource\n");
|
|
return -ENOMEM;
|
|
}
|
|
dvo->regs = devm_ioremap(dev, res->start,
|
|
resource_size(res));
|
|
if (!dvo->regs)
|
|
return -ENOMEM;
|
|
|
|
dvo->clk_pix = devm_clk_get(dev, "dvo_pix");
|
|
if (IS_ERR(dvo->clk_pix)) {
|
|
DRM_ERROR("Cannot get dvo_pix clock\n");
|
|
return PTR_ERR(dvo->clk_pix);
|
|
}
|
|
|
|
dvo->clk = devm_clk_get(dev, "dvo");
|
|
if (IS_ERR(dvo->clk)) {
|
|
DRM_ERROR("Cannot get dvo clock\n");
|
|
return PTR_ERR(dvo->clk);
|
|
}
|
|
|
|
dvo->clk_main_parent = devm_clk_get(dev, "main_parent");
|
|
if (IS_ERR(dvo->clk_main_parent)) {
|
|
DRM_DEBUG_DRIVER("Cannot get main_parent clock\n");
|
|
dvo->clk_main_parent = NULL;
|
|
}
|
|
|
|
dvo->clk_aux_parent = devm_clk_get(dev, "aux_parent");
|
|
if (IS_ERR(dvo->clk_aux_parent)) {
|
|
DRM_DEBUG_DRIVER("Cannot get aux_parent clock\n");
|
|
dvo->clk_aux_parent = NULL;
|
|
}
|
|
|
|
dvo->panel_node = of_parse_phandle(np, "sti,panel", 0);
|
|
if (!dvo->panel_node)
|
|
DRM_ERROR("No panel associated to the dvo output\n");
|
|
of_node_put(dvo->panel_node);
|
|
|
|
platform_set_drvdata(pdev, dvo);
|
|
|
|
return component_add(&pdev->dev, &sti_dvo_ops);
|
|
}
|
|
|
|
static int sti_dvo_remove(struct platform_device *pdev)
|
|
{
|
|
component_del(&pdev->dev, &sti_dvo_ops);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id dvo_of_match[] = {
|
|
{ .compatible = "st,stih407-dvo", },
|
|
{ /* end node */ }
|
|
};
|
|
MODULE_DEVICE_TABLE(of, dvo_of_match);
|
|
|
|
struct platform_driver sti_dvo_driver = {
|
|
.driver = {
|
|
.name = "sti-dvo",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = dvo_of_match,
|
|
},
|
|
.probe = sti_dvo_probe,
|
|
.remove = sti_dvo_remove,
|
|
};
|
|
|
|
MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
|
|
MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
|
|
MODULE_LICENSE("GPL");
|