drm/rockchip: rk618: refactor display pipeline

It's hard to do platform specific initialization in the current pipeline.
This patch convert to generic bridge interface and let it attach to
a platform specific encoder.

   CRTC    -->  Encoder  --> Bridge --> Connector --> Panel
(platform)

This patch changed the pipeline to:

   CRTC    -->  Encoder  --> Bridge --> Connector --> Panel
(platform)     (platform)

Change-Id: I43e0dab05e41965767f55cfe15b3674a71911312
Signed-off-by: Wyon Bi <bivvy.bi@rock-chips.com>
This commit is contained in:
Wyon Bi
2018-11-22 19:17:21 +08:00
parent 90c831e971
commit 0b5d5eafb1
8 changed files with 763 additions and 275 deletions

View File

@@ -21,7 +21,9 @@ bindings specified in Documentation/devicetree/bindings/graph.txt.
Example:
&rk618 {
VIF: vif {
status = "okay";
vif {
compatible = "rockchip,rk618-vif";
clocks = <&CRU VIF0_CLK>, <&CRU VIF0_PRE_CLK>;
clock-names = "vif", "vif_pre";
@@ -34,23 +36,74 @@ Example:
port@0 {
reg = <0>;
vif_input_hdmi: endpoint {
remote-endpoint = <&hdmi_output_vif>;
bridge_input_rgb: endpoint {
remote-endpoint = <&rgb_out_bridge>;
};
};
port@1 {
reg = <1>;
vif_output_lvds: endpoint {
remote-endpoint = <&lvds_input_vif>;
vif_output_hdmi: endpoint {
remote-endpoint = <&hdmi_input_vif>;
};
};
};
};
};
LVDS Encoder
SCALER Module
----------
Required properties:
- compatible: value should be one of the following:
"rockchip,rk618-scaler"
- clocks: must include clock specifiers corresponding to entries in the
clock-names property.
See ../clocks/clock-bindings.txt for details.
- clock-names: list of clock names sorted in the same order as the clocks
property. Must contain "scaler", "vif", "dither".
Required nodes:
The connections to the video ports are modeled using the OF graph
bindings specified in Documentation/devicetree/bindings/graph.txt.
Example:
&rk618 {
status = "okay";
scaler {
compatible = "rockchip,rk618-scaler";
clocks = <&CRU SCALER_CLK>, <&CRU DITHER_CLK>, <&CRU VIF0_CLK>;
clock-names = "scaler", "dither", "vif";
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
scaler_input_hdmi: endpoint {
remote-endpoint = <&hdmi_output_scaler>;
};
};
port@1 {
reg = <1>;
scaler_output_lvds: endpoint {
remote-endpoint = <&lvds_input_scaler>;
};
};
};
};
};
LVDS Connector
------------
Required properties:
@@ -60,7 +113,10 @@ Required properties:
clock-names property.
See ../clocks/clock-bindings.txt for details.
- clock-names: list of clock names sorted in the same order as the clocks
property. Must contain "lvds", "dither", "vif", "scaler".
property. Must contain "lvds".
Optional properties:
- dual-channel: boolean. if it exists, enable dual channel mode
Required nodes:
@@ -74,9 +130,8 @@ Example:
lvds {
compatible = "rockchip,rk618-lvds";
clocks = <&CRU LVDS_CLK>, <&CRU DITHER_CLK>,
<&CRU VIF0_CLK>, <&CRU SCALER_CLK>;
clock-names = "lvds", "dither", "vif", "scaler";
clocks = <&CRU LVDS_CLK>;
clock-names = "lvds";
status = "okay";
ports {
@@ -85,22 +140,9 @@ Example:
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
lvds_input_vop: endpoint@0 {
reg = <0>;
remote-endpoint = <&vop_output_lvds>;
};
lvds_input_vif: endpoint@1 {
reg = <1>;
remote-endpoint = <&vif_output_lvds>;
};
lvds_input_hdmi: endpoint@2 {
reg = <2>;
remote-endpoint = <&hdmi_output_lvds>;
bridge_input_rgb: endpoint {
remote-endpoint = <&rgb_out_bridge>;
};
};
@@ -115,7 +157,7 @@ Example:
};
};
RGB Encoder
RGB Connector
------------
Required properties:
@@ -125,7 +167,7 @@ Required properties:
clock-names property.
See ../clocks/clock-bindings.txt for details.
- clock-names: list of clock names sorted in the same order as the clocks
property. Must contain "rgb", "dither", "vif", "scaler".
property. Must contain "rgb".
Required nodes:
@@ -139,9 +181,8 @@ Example:
rgb {
compatible = "rockchip,rk618-rgb";
clocks = <&CRU RGB_CLK>, <&CRU DITHER_CLK>,
<&CRU VIF0_CLK>, <&CRU SCALER_CLK>;
clock-names = "rgb", "dither", "vif", "scaler";
clocks = <&CRU RGB_CLK>;
clock-names = "rgb";
status = "okay";
ports {
@@ -150,22 +191,9 @@ Example:
port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
rgb_input_vop: endpoint@0 {
reg = <0>;
remote-endpoint = <&vop_output_rgb>;
};
rgb_input_vif: endpoint@1 {
reg = <1>;
remote-endpoint = <&vif_output_rgb>;
};
rgb_input_hdmi: endpoint@2 {
reg = <2>;
remote-endpoint = <&hdmi_output_rgb>;
bridge_input_rgb: endpoint {
remote-endpoint = <&rgb_out_bridge>;
};
};
@@ -173,16 +201,12 @@ Example:
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
rgb_lcdc1_out_tve: endpoint@0 {
rgb_output_panel: endpoint@0 {
reg = <0>;
remote-endpoint = <&tve_in_rgb_lcdc1>;
};
rgb_out_tve: endpoint@1 {
status = "disabled";
reg = <1>;
remote-endpoint = <&tve_in_rgb>;
remote-endpoint = <&panel_input_rgb>;
};
};
};
};
};
};

View File

@@ -3,9 +3,8 @@
# Makefile for the Rockchip RK618 display bridge driver.
#
obj-$(CONFIG_DRM_ROCKCHIP_RK618) += rk618_output.o \
rk618_scaler.o \
obj-$(CONFIG_DRM_ROCKCHIP_RK618) += rk618_scaler.o \
rk618_vif.o \
rk618_dither.o \
rk618_lvds.o \
rk618_rgb.o \
rk618_rgb.o

View File

@@ -1,18 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
*
* 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.
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
*/
#include "rk618_output.h"
#include "rk618_dither.h"
#define RK618_FRC_REG 0x0054
#define FRC_DEN_INV HIWORD_UPDATE(1, 6, 6)
@@ -26,20 +19,20 @@
#define FRC_DITHER_ENABLE HIWORD_UPDATE(1, 0, 0)
#define FRC_DITHER_DISABLE HIWORD_UPDATE(0, 0, 0)
void rk618_dither_disable(struct rk618 *rk618)
void rk618_frc_dither_disable(struct rk618 *rk618)
{
regmap_write(rk618->regmap, RK618_FRC_REG, FRC_DITHER_DISABLE);
}
EXPORT_SYMBOL_GPL(rk618_dither_disable);
EXPORT_SYMBOL_GPL(rk618_frc_dither_disable);
void rk618_dither_enable(struct rk618 *rk618)
void rk618_frc_dither_enable(struct rk618 *rk618)
{
regmap_write(rk618->regmap, RK618_FRC_REG, FRC_DITHER_ENABLE);
}
EXPORT_SYMBOL_GPL(rk618_dither_enable);
EXPORT_SYMBOL_GPL(rk618_frc_dither_enable);
void rk618_dither_frc_dclk_invert(struct rk618 *rk618)
void rk618_frc_dclk_invert(struct rk618 *rk618)
{
regmap_write(rk618->regmap, RK618_FRC_REG, FRC_DCLK_INV);
}
EXPORT_SYMBOL_GPL(rk618_dither_frc_dclk_invert);
EXPORT_SYMBOL_GPL(rk618_frc_dclk_invert);

View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2018 Rockchip Electronics Co. Ltd.
*
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
*/
#ifndef _RK618_DITHER_H_
#define _RK618_DITHER_H_
#include <linux/mfd/rk618.h>
void rk618_frc_dither_disable(struct rk618 *rk618);
void rk618_frc_dither_enable(struct rk618 *rk618);
void rk618_frc_dclk_invert(struct rk618 *rk618);
#endif

View File

@@ -1,20 +1,28 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
*
* 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.
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
*/
#include "rk618_output.h"
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/mfd/rk618.h>
#define IS_DOUBLE_CHANNEL(lvds) ((lvds)->channels == 2)
#include <drm/drmP.h>
#include <drm/drm_of.h>
#include <drm/drm_atomic.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_panel.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
#include "rk618_dither.h"
enum {
LVDS_8BIT_MODE_FORMAT_1,
@@ -24,31 +32,86 @@ enum {
};
struct rk618_lvds {
struct rk618_output base;
struct drm_bridge base;
struct drm_connector connector;
struct drm_panel *panel;
struct device *dev;
struct regmap *regmap;
struct clk *clock;
u32 channels;
struct rk618 *parent;
bool dual_channel;
u32 format;
};
static inline struct rk618_lvds *to_lvds(struct rk618_output *output)
static inline struct rk618_lvds *bridge_to_lvds(struct drm_bridge *b)
{
return container_of(output, struct rk618_lvds, base);
return container_of(b, struct rk618_lvds, base);
}
static void rk618_lvds_enable(struct rk618_output *output)
static inline struct rk618_lvds *connector_to_lvds(struct drm_connector *c)
{
struct rk618_lvds *lvds = to_lvds(output);
return container_of(c, struct rk618_lvds, connector);
}
static struct drm_encoder *
rk618_lvds_connector_best_encoder(struct drm_connector *connector)
{
struct rk618_lvds *lvds = connector_to_lvds(connector);
return lvds->base.encoder;
}
static int rk618_lvds_connector_get_modes(struct drm_connector *connector)
{
struct rk618_lvds *lvds = connector_to_lvds(connector);
return drm_panel_get_modes(lvds->panel);
}
static const struct drm_connector_helper_funcs
rk618_lvds_connector_helper_funcs = {
.get_modes = rk618_lvds_connector_get_modes,
.best_encoder = rk618_lvds_connector_best_encoder,
};
static enum drm_connector_status
rk618_lvds_connector_detect(struct drm_connector *connector, bool force)
{
return connector_status_connected;
}
static void rk618_lvds_connector_destroy(struct drm_connector *connector)
{
struct rk618_lvds *lvds = connector_to_lvds(connector);
drm_panel_detach(lvds->panel);
drm_connector_cleanup(connector);
}
static const struct drm_connector_funcs rk618_lvds_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.detect = rk618_lvds_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = rk618_lvds_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 void rk618_lvds_bridge_enable(struct drm_bridge *bridge)
{
struct rk618_lvds *lvds = bridge_to_lvds(bridge);
u32 value;
clk_prepare_enable(lvds->clock);
rk618_frc_dclk_invert(lvds->parent);
value = LVDS_CON_CHA0TTL_DISABLE | LVDS_CON_CHA1TTL_DISABLE |
LVDS_CON_CHA0_POWER_UP | LVDS_CON_CBG_POWER_UP |
LVDS_CON_PLL_POWER_UP | LVDS_CON_SELECT(lvds->format);
if (IS_DOUBLE_CHANNEL(lvds))
if (lvds->dual_channel)
value |= LVDS_CON_CHA1_POWER_UP | LVDS_DCLK_INV |
LVDS_CON_CHASEL_DOUBLE_CHANNEL;
else
@@ -56,11 +119,17 @@ static void rk618_lvds_enable(struct rk618_output *output)
LVDS_CON_CHASEL_SINGLE_CHANNEL;
regmap_write(lvds->regmap, RK618_LVDS_CON, value);
drm_panel_prepare(lvds->panel);
drm_panel_enable(lvds->panel);
}
static void rk618_lvds_disable(struct rk618_output *output)
static void rk618_lvds_bridge_disable(struct drm_bridge *bridge)
{
struct rk618_lvds *lvds = to_lvds(output);
struct rk618_lvds *lvds = bridge_to_lvds(bridge);
drm_panel_disable(lvds->panel);
drm_panel_unprepare(lvds->panel);
regmap_write(lvds->regmap, RK618_LVDS_CON,
LVDS_CON_CHA0_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN |
@@ -69,17 +138,21 @@ static void rk618_lvds_disable(struct rk618_output *output)
clk_disable_unprepare(lvds->clock);
}
static void rk618_lvds_mode_set(struct rk618_output *output,
const struct drm_display_mode *mode)
static void rk618_lvds_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adj)
{
struct rk618_lvds *lvds = to_lvds(output);
struct rk618_lvds *lvds = bridge_to_lvds(bridge);
struct drm_connector *connector = &lvds->connector;
struct drm_display_info *info = &connector->display_info;
u32 bus_format;
if (mode->hdisplay > 1366 || mode->vdisplay > 1366)
lvds->channels = 2;
if (info->num_bus_formats)
bus_format = info->bus_formats[0];
else
lvds->channels = 1;
bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
switch (output->bus_format) {
switch (bus_format) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: /* jeida-18 */
lvds->format = LVDS_6BIT_MODE;
break;
@@ -95,39 +168,57 @@ static void rk618_lvds_mode_set(struct rk618_output *output,
}
}
static const struct rk618_output_funcs rk618_lvds_funcs = {
.enable = rk618_lvds_enable,
.disable = rk618_lvds_disable,
.mode_set = rk618_lvds_mode_set,
};
static int rk618_lvds_bind(struct device *dev, struct device *master,
void *data)
static int rk618_lvds_bridge_attach(struct drm_bridge *bridge)
{
struct drm_device *drm = data;
struct rk618_lvds *lvds = dev_get_drvdata(dev);
struct rk618_lvds *lvds = bridge_to_lvds(bridge);
struct device *dev = lvds->dev;
struct drm_connector *connector = &lvds->connector;
struct drm_device *drm = bridge->dev;
int ret;
return rk618_output_bind(&lvds->base, drm, DRM_MODE_ENCODER_LVDS,
connector->port = dev->of_node;
ret = drm_connector_init(drm, connector, &rk618_lvds_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
if (ret) {
dev_err(lvds->dev, "Failed to initialize connector with drm\n");
return ret;
}
drm_connector_helper_add(connector, &rk618_lvds_connector_helper_funcs);
drm_mode_connector_attach_encoder(connector, bridge->encoder);
ret = drm_panel_attach(lvds->panel, connector);
if (ret) {
dev_err(lvds->dev, "Failed to attach panel\n");
return ret;
}
return 0;
}
static void rk618_lvds_unbind(struct device *dev, struct device *master,
void *data)
{
struct rk618_lvds *lvds = dev_get_drvdata(dev);
rk618_output_unbind(&lvds->base);
}
static const struct component_ops rk618_lvds_component_ops = {
.bind = rk618_lvds_bind,
.unbind = rk618_lvds_unbind,
static const struct drm_bridge_funcs rk618_lvds_bridge_funcs = {
.attach = rk618_lvds_bridge_attach,
.mode_set = rk618_lvds_bridge_mode_set,
.enable = rk618_lvds_bridge_enable,
.disable = rk618_lvds_bridge_disable,
};
static int rk618_lvds_parse_dt(struct rk618_lvds *lvds)
{
struct device *dev = lvds->dev;
lvds->dual_channel = of_property_read_bool(dev->of_node,
"dual-channel");
return 0;
}
static int rk618_lvds_probe(struct platform_device *pdev)
{
struct rk618 *rk618 = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct device_node *endpoint;
struct rk618_lvds *lvds;
int ret;
@@ -139,9 +230,38 @@ static int rk618_lvds_probe(struct platform_device *pdev)
return -ENOMEM;
lvds->dev = dev;
lvds->regmap = rk618->regmap;
lvds->parent = rk618;
platform_set_drvdata(pdev, lvds);
ret = rk618_lvds_parse_dt(lvds);
if (ret) {
dev_err(dev, "failed to parse DT\n");
return ret;
}
lvds->regmap = dev_get_regmap(dev->parent, NULL);
if (!lvds->regmap)
return -ENODEV;
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
if (endpoint) {
struct device_node *remote;
remote = of_graph_get_remote_port_parent(endpoint);
of_node_put(endpoint);
if (!remote) {
dev_err(dev, "no panel connected\n");
return -ENODEV;
}
lvds->panel = of_drm_find_panel(remote);
of_node_put(remote);
if (!lvds->panel) {
dev_err(dev, "Waiting for panel driver\n");
return -EPROBE_DEFER;
}
}
lvds->clock = devm_clk_get(dev, "lvds");
if (IS_ERR(lvds->clock)) {
ret = PTR_ERR(lvds->clock);
@@ -149,21 +269,22 @@ static int rk618_lvds_probe(struct platform_device *pdev)
return ret;
}
lvds->base.parent = rk618;
lvds->base.dev = dev;
lvds->base.funcs = &rk618_lvds_funcs;
ret = rk618_output_register(&lvds->base);
if (ret)
lvds->base.funcs = &rk618_lvds_bridge_funcs;
lvds->base.of_node = dev->of_node;
ret = drm_bridge_add(&lvds->base);
if (ret) {
dev_err(dev, "failed to add drm_bridge: %d\n", ret);
return ret;
}
return component_add(dev, &rk618_lvds_component_ops);
return 0;
}
static int rk618_lvds_remove(struct platform_device *pdev)
{
struct rk618_lvds *lvds = platform_get_drvdata(pdev);
component_del(lvds->dev, &rk618_lvds_component_ops);
drm_bridge_remove(&lvds->base);
return 0;
}

View File

@@ -1,36 +1,115 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018 Rockchip Electronics Co. Ltd.
*
* Author: Chen Shunqing <csq@rock-chips.com>
*/
#include "rk618_output.h"
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <video/of_display_timing.h>
#include <linux/regmap.h>
#include <linux/mfd/rk618.h>
#include <drm/drmP.h>
#include <drm/drm_of.h>
#include <drm/drm_atomic.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_panel.h>
#include <video/videomode.h>
#include "rk618_dither.h"
struct rk618_rgb {
struct rk618_output base;
struct drm_bridge base;
struct drm_connector connector;
struct drm_panel *panel;
struct drm_bridge *bridge;
struct device *dev;
struct regmap *regmap;
struct clk *clock;
struct rk618 *parent;
u32 id;
};
static inline struct rk618_rgb *to_rgb(struct rk618_output *output)
static inline struct rk618_rgb *bridge_to_rgb(struct drm_bridge *b)
{
return container_of(output, struct rk618_rgb, base);
return container_of(b, struct rk618_rgb, base);
}
static void rk618_rgb_enable(struct rk618_output *output)
static inline struct rk618_rgb *connector_to_rgb(struct drm_connector *c)
{
struct rk618_rgb *rgb = to_rgb(output);
u32 value;
struct device_node *endpoint;
int lcdc1_output_rgb = 0;
return container_of(c, struct rk618_rgb, connector);
}
endpoint = of_graph_get_endpoint_by_regs(output->dev->of_node, 1, 0);
if (endpoint && of_device_is_available(endpoint))
lcdc1_output_rgb = 1;
static struct drm_encoder *
rk618_rgb_connector_best_encoder(struct drm_connector *connector)
{
struct rk618_rgb *rgb = connector_to_rgb(connector);
return rgb->base.encoder;
}
static int rk618_rgb_connector_get_modes(struct drm_connector *connector)
{
struct rk618_rgb *rgb = connector_to_rgb(connector);
return drm_panel_get_modes(rgb->panel);
}
static const struct drm_connector_helper_funcs
rk618_rgb_connector_helper_funcs = {
.get_modes = rk618_rgb_connector_get_modes,
.best_encoder = rk618_rgb_connector_best_encoder,
};
static enum drm_connector_status
rk618_rgb_connector_detect(struct drm_connector *connector, bool force)
{
return connector_status_connected;
}
static void rk618_rgb_connector_destroy(struct drm_connector *connector)
{
struct rk618_rgb *rgb = connector_to_rgb(connector);
drm_panel_detach(rgb->panel);
drm_connector_cleanup(connector);
}
static const struct drm_connector_funcs rk618_rgb_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms,
.detect = rk618_rgb_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = rk618_rgb_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 void rk618_rgb_bridge_enable(struct drm_bridge *bridge)
{
struct rk618_rgb *rgb = bridge_to_rgb(bridge);
u32 value;
clk_prepare_enable(rgb->clock);
if (lcdc1_output_rgb) {
rk618_frc_dclk_invert(rgb->parent);
dev_dbg(rgb->dev, "id=%d\n", rgb->id);
if (rgb->id) {
value = LVDS_CON_CBG_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN |
LVDS_CON_CHA0_POWER_DOWN | LVDS_CON_CHA0TTL_ENABLE |
LVDS_CON_CHA1TTL_ENABLE | LVDS_CON_PLL_POWER_DOWN;
regmap_write(rgb->regmap, RK618_LVDS_CON, value);
regmap_write(rgb->regmap, RK618_IO_CON0, PORT2_OUTPUT_TTL);
} else {
value = LVDS_CON_CHA1TTL_DISABLE | LVDS_CON_CHA0TTL_DISABLE |
LVDS_CON_CHA1_POWER_DOWN | LVDS_CON_CHA0_POWER_DOWN |
LVDS_CON_CBG_POWER_DOWN | LVDS_CON_PLL_POWER_DOWN;
@@ -38,68 +117,86 @@ static void rk618_rgb_enable(struct rk618_output *output)
regmap_write(rgb->regmap, RK618_IO_CON0,
PORT1_OUTPUT_TTL_ENABLE);
} else {
value = LVDS_CON_CBG_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN |
LVDS_CON_CHA0_POWER_DOWN;
value |= LVDS_CON_CHA0TTL_ENABLE | LVDS_CON_CHA1TTL_ENABLE |
LVDS_CON_PLL_POWER_DOWN;
regmap_write(rgb->regmap, RK618_LVDS_CON, value);
}
regmap_write(rgb->regmap, RK618_IO_CON0, PORT2_OUTPUT_TTL);
if (rgb->panel) {
drm_panel_prepare(rgb->panel);
drm_panel_enable(rgb->panel);
}
}
static void rk618_rgb_disable(struct rk618_output *output)
static void rk618_rgb_bridge_disable(struct drm_bridge *bridge)
{
struct rk618_rgb *rgb = to_rgb(output);
struct rk618_rgb *rgb = bridge_to_rgb(bridge);
regmap_write(rgb->regmap, RK618_LVDS_CON,
LVDS_CON_CHA0_POWER_DOWN | LVDS_CON_CHA1_POWER_DOWN |
LVDS_CON_CBG_POWER_DOWN | LVDS_CON_PLL_POWER_DOWN);
if (rgb->panel) {
drm_panel_disable(rgb->panel);
drm_panel_unprepare(rgb->panel);
}
if (rgb->id)
regmap_write(rgb->regmap, RK618_LVDS_CON,
LVDS_CON_CHA0_POWER_DOWN |
LVDS_CON_CHA1_POWER_DOWN |
LVDS_CON_CBG_POWER_DOWN |
LVDS_CON_PLL_POWER_DOWN);
else
regmap_write(rgb->regmap, RK618_IO_CON0,
PORT1_OUTPUT_TTL_DISABLE);
clk_disable_unprepare(rgb->clock);
}
static const struct rk618_output_funcs rk618_rgb_funcs = {
.enable = rk618_rgb_enable,
.disable = rk618_rgb_disable,
};
static int rk618_rgb_bind(struct device *dev, struct device *master,
void *data)
static int rk618_rgb_bridge_attach(struct drm_bridge *bridge)
{
struct drm_device *drm = data;
struct rk618_rgb *rgb = dev_get_drvdata(dev);
struct rk618_rgb *rgb = bridge_to_rgb(bridge);
struct device *dev = rgb->dev;
struct drm_connector *connector = &rgb->connector;
struct drm_device *drm = bridge->dev;
int ret;
return rk618_output_bind(&rgb->base, drm, DRM_MODE_ENCODER_LVDS,
DRM_MODE_CONNECTOR_LVDS);
if (rgb->panel) {
connector->port = dev->of_node;
ret = drm_connector_init(drm, connector,
&rk618_rgb_connector_funcs,
DRM_MODE_CONNECTOR_DPI);
if (ret) {
dev_err(dev, "Failed to initialize connector\n");
return ret;
}
drm_connector_helper_add(connector,
&rk618_rgb_connector_helper_funcs);
drm_mode_connector_attach_encoder(connector, bridge->encoder);
drm_panel_attach(rgb->panel, connector);
} else {
rgb->bridge->encoder = bridge->encoder;
ret = drm_bridge_attach(bridge->dev, rgb->bridge);
if (ret) {
dev_err(dev, "failed to attach bridge\n");
return ret;
}
bridge->next = rgb->bridge;
}
return 0;
}
static void rk618_rgb_unbind(struct device *dev, struct device *master,
void *data)
{
struct rk618_rgb *rgb = dev_get_drvdata(dev);
rk618_output_unbind(&rgb->base);
}
static const struct component_ops rk618_rgb_component_ops = {
.bind = rk618_rgb_bind,
.unbind = rk618_rgb_unbind,
static const struct drm_bridge_funcs rk618_rgb_bridge_funcs = {
.attach = rk618_rgb_bridge_attach,
.enable = rk618_rgb_bridge_enable,
.disable = rk618_rgb_bridge_disable,
};
static const struct of_device_id rk618_rgb_of_match[] = {
{ .compatible = "rockchip,rk618-rgb", },
{},
};
MODULE_DEVICE_TABLE(of, rk618_rgb_of_match);
static int rk618_rgb_probe(struct platform_device *pdev)
{
struct rk618 *rk618 = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct rk618_rgb *rgb;
int ret;
int id, ret;
if (!of_device_is_available(dev->of_node))
return -ENODEV;
@@ -109,9 +206,13 @@ static int rk618_rgb_probe(struct platform_device *pdev)
return -ENOMEM;
rgb->dev = dev;
rgb->regmap = rk618->regmap;
rgb->parent = rk618;
platform_set_drvdata(pdev, rgb);
rgb->regmap = dev_get_regmap(dev->parent, NULL);
if (!rgb->regmap)
return -ENODEV;
rgb->clock = devm_clk_get(dev, "rgb");
if (IS_ERR(rgb->clock)) {
ret = PTR_ERR(rgb->clock);
@@ -119,25 +220,58 @@ static int rk618_rgb_probe(struct platform_device *pdev)
return ret;
}
rgb->base.parent = rk618;
rgb->base.dev = dev;
rgb->base.funcs = &rk618_rgb_funcs;
ret = rk618_output_register(&rgb->base);
if (ret)
return ret;
for (id = 0; id < 2; id++) {
struct device_node *remote, *endpoint;
return component_add(dev, &rk618_rgb_component_ops);
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, id);
if (!endpoint)
continue;
remote = of_graph_get_remote_port_parent(endpoint);
of_node_put(endpoint);
if (!remote) {
dev_err(dev, "no panel/bridge connected\n");
return -ENODEV;
}
rgb->panel = of_drm_find_panel(remote);
if (!rgb->panel)
rgb->bridge = of_drm_find_bridge(remote);
of_node_put(remote);
if (!rgb->panel && !rgb->bridge) {
dev_err(dev, "Waiting for panel/bridge driver\n");
return -EPROBE_DEFER;
}
rgb->id = id;
}
rgb->base.funcs = &rk618_rgb_bridge_funcs;
rgb->base.of_node = dev->of_node;
ret = drm_bridge_add(&rgb->base);
if (ret) {
dev_err(dev, "failed to add drm_bridge: %d\n", ret);
return ret;
}
return 0;
}
static int rk618_rgb_remove(struct platform_device *pdev)
{
struct rk618_rgb *rgb = platform_get_drvdata(pdev);
component_del(rgb->dev, &rk618_rgb_component_ops);
drm_bridge_remove(&rgb->base);
return 0;
}
static const struct of_device_id rk618_rgb_of_match[] = {
{ .compatible = "rockchip,rk618-rgb", },
{},
};
MODULE_DEVICE_TABLE(of, rk618_rgb_of_match);
static struct platform_driver rk618_rgb_driver = {
.driver = {
.name = "rk618-rgb",
@@ -148,6 +282,7 @@ static struct platform_driver rk618_rgb_driver = {
};
module_platform_driver(rk618_rgb_driver);
MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
MODULE_AUTHOR("Chen Shunqing <csq@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip RK618 RGB driver");
MODULE_LICENSE("GPL v2");

View File

@@ -1,18 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
*
* 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.
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
*/
#include "rk618_output.h"
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mfd/rk618.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <drm/drm_of.h>
#include <drm/drmP.h>
#include <video/videomode.h>
#define RK618_SCALER_REG0 0x0030
#define SCL_VER_DOWN_MODE(x) HIWORD_UPDATE(x, 8, 8)
@@ -47,17 +51,31 @@
#define DSP_VBOR_END(x) UPDATE(x, 27, 16)
#define DSP_VBOR_ST(x) UPDATE(x, 11, 0)
void rk618_scaler_enable(struct rk618 *rk618)
{
regmap_write(rk618->regmap, RK618_SCALER_REG0, SCL_ENABLE);
}
EXPORT_SYMBOL(rk618_scaler_enable);
struct rk618_scaler {
struct drm_bridge base;
struct drm_bridge *bridge;
struct drm_display_mode mode;
struct device *dev;
struct regmap *regmap;
struct clk *vif_clk;
struct clk *dither_clk;
struct clk *scaler_clk;
};
void rk618_scaler_disable(struct rk618 *rk618)
static inline struct rk618_scaler *bridge_to_scaler(struct drm_bridge *bridge)
{
regmap_write(rk618->regmap, RK618_SCALER_REG0, SCL_DISABLE);
return container_of(bridge, struct rk618_scaler, base);
}
static void rk618_scaler_enable(struct rk618_scaler *scl)
{
regmap_write(scl->regmap, RK618_SCALER_REG0, SCL_ENABLE);
}
static void rk618_scaler_disable(struct rk618_scaler *scl)
{
regmap_write(scl->regmap, RK618_SCALER_REG0, SCL_DISABLE);
}
EXPORT_SYMBOL(rk618_scaler_disable);
static void calc_dsp_frm_hst_vst(const struct videomode *src,
const struct videomode *dst,
@@ -68,7 +86,7 @@ static void calc_dsp_frm_hst_vst(const struct videomode *src,
long long t_frm_st;
u64 t_bp_in, t_bp_out, t_delta, tin;
u32 src_pixclock, dst_pixclock;
u32 dsp_htotal, dsp_vtotal, src_htotal, src_vtotal;
u32 dsp_htotal, src_htotal, src_vtotal;
src_pixclock = div_u64(1000000000000llu, src->pixelclock);
dst_pixclock = div_u64(1000000000000llu, dst->pixelclock);
@@ -79,8 +97,6 @@ static void calc_dsp_frm_hst_vst(const struct videomode *src,
src->vfront_porch;
dsp_htotal = dst->hsync_len + dst->hback_porch + dst->hactive +
dst->hfront_porch;
dsp_vtotal = dst->vsync_len + dst->vback_porch + dst->vactive +
dst->vfront_porch;
bp_in = (src->vback_porch + src->vsync_len) * src_htotal +
src->hsync_len + src->hback_porch;
@@ -107,29 +123,23 @@ static void calc_dsp_frm_hst_vst(const struct videomode *src,
*dsp_frame_vst = t_frm_st;
}
void rk618_scaler_configure(struct rk618 *rk618,
const struct drm_display_mode *scale_mode,
const struct drm_display_mode *panel_mode)
static void rk618_scaler_init(struct rk618_scaler *scl,
const struct drm_display_mode *s,
const struct drm_display_mode *d)
{
struct device *dev = rk618->dev;
struct videomode src, dst;
u32 dsp_frame_hst, dsp_frame_vst;
u32 scl_hor_mode, scl_ver_mode;
u32 scl_v_factor, scl_h_factor;
u32 src_htotal, src_vtotal;
u32 dsp_htotal, dsp_hs_end, dsp_hact_st, dsp_hact_end;
u32 dsp_vtotal, dsp_vs_end, dsp_vact_st, dsp_vact_end;
u32 dsp_hbor_end, dsp_hbor_st, dsp_vbor_end, dsp_vbor_st;
u16 bor_right = 0, bor_left = 0, bor_up = 0, bor_down = 0;
u8 hor_down_mode = 0, ver_down_mode = 0;
drm_display_mode_to_videomode(scale_mode, &src);
drm_display_mode_to_videomode(panel_mode, &dst);
drm_display_mode_to_videomode(s, &src);
drm_display_mode_to_videomode(d, &dst);
src_htotal = src.hsync_len + src.hback_porch + src.hactive +
src.hfront_porch;
src_vtotal = src.vsync_len + src.vback_porch + src.vactive +
src.vfront_porch;
dsp_htotal = dst.hsync_len + dst.hback_porch + dst.hactive +
dst.hfront_porch;
dsp_vtotal = dst.vsync_len + dst.vback_porch + dst.vactive +
@@ -146,7 +156,7 @@ void rk618_scaler_configure(struct rk618 *rk618,
dsp_vact_end = dsp_vbor_end - bor_down;
calc_dsp_frm_hst_vst(&src, &dst, &dsp_frame_hst, &dsp_frame_vst);
dev_dbg(dev, "dsp_frame_vst=%d, dsp_frame_hst=%d\n",
dev_dbg(scl->dev, "dsp_frame_vst=%d, dsp_frame_hst=%d\n",
dsp_frame_vst, dsp_frame_hst);
if (src.hactive > dst.hactive) {
@@ -164,17 +174,17 @@ void rk618_scaler_configure(struct rk618 *rk618,
(src.hactive - 1);
}
dev_dbg(rk618->dev, "horizontal scale down\n");
dev_dbg(scl->dev, "horizontal scale down\n");
} else if (src.hactive == dst.hactive) {
scl_hor_mode = 0;
scl_h_factor = 0;
dev_dbg(rk618->dev, "horizontal no scale\n");
dev_dbg(scl->dev, "horizontal no scale\n");
} else {
scl_hor_mode = 1;
scl_h_factor = ((src.hactive - 1) << 16) / (dst.hactive - 1);
dev_dbg(rk618->dev, "horizontal scale up\n");
dev_dbg(scl->dev, "horizontal scale up\n");
}
if (src.vactive > dst.vactive) {
@@ -192,37 +202,223 @@ void rk618_scaler_configure(struct rk618 *rk618,
(src.vactive - 1);
}
dev_dbg(rk618->dev, "vertical scale down\n");
dev_dbg(scl->dev, "vertical scale down\n");
} else if (src.vactive == dst.vactive) {
scl_ver_mode = 0;
scl_v_factor = 0;
dev_dbg(rk618->dev, "vertical no scale\n");
dev_dbg(scl->dev, "vertical no scale\n");
} else {
scl_ver_mode = 1;
scl_v_factor = ((src.vactive - 1) << 16) / (dst.vactive - 1);
dev_dbg(rk618->dev, "vertical scale up\n");
dev_dbg(scl->dev, "vertical scale up\n");
}
regmap_write(rk618->regmap, RK618_SCALER_REG0,
regmap_write(scl->regmap, RK618_SCALER_REG0,
SCL_VER_MODE(scl_ver_mode) | SCL_HOR_MODE(scl_hor_mode));
regmap_write(rk618->regmap, RK618_SCALER_REG1,
regmap_write(scl->regmap, RK618_SCALER_REG1,
SCL_V_FACTOR(scl_v_factor) | SCL_H_FACTOR(scl_h_factor));
regmap_write(rk618->regmap, RK618_SCALER_REG2,
regmap_write(scl->regmap, RK618_SCALER_REG2,
DSP_FRAME_VST(dsp_frame_vst) |
DSP_FRAME_HST(dsp_frame_hst));
regmap_write(rk618->regmap, RK618_SCALER_REG3,
regmap_write(scl->regmap, RK618_SCALER_REG3,
DSP_HS_END(dsp_hs_end) | DSP_HTOTAL(dsp_htotal));
regmap_write(rk618->regmap, RK618_SCALER_REG4,
regmap_write(scl->regmap, RK618_SCALER_REG4,
DSP_HACT_END(dsp_hact_end) | DSP_HACT_ST(dsp_hact_st));
regmap_write(rk618->regmap, RK618_SCALER_REG5,
regmap_write(scl->regmap, RK618_SCALER_REG5,
DSP_VS_END(dsp_vs_end) | DSP_VTOTAL(dsp_vtotal));
regmap_write(rk618->regmap, RK618_SCALER_REG6,
regmap_write(scl->regmap, RK618_SCALER_REG6,
DSP_VACT_END(dsp_vact_end) | DSP_VACT_ST(dsp_vact_st));
regmap_write(rk618->regmap, RK618_SCALER_REG7,
regmap_write(scl->regmap, RK618_SCALER_REG7,
DSP_HBOR_END(dsp_hbor_end) | DSP_HBOR_ST(dsp_hbor_st));
regmap_write(rk618->regmap, RK618_SCALER_REG8,
regmap_write(scl->regmap, RK618_SCALER_REG8,
DSP_VBOR_END(dsp_vbor_end) | DSP_VBOR_ST(dsp_vbor_st));
}
EXPORT_SYMBOL(rk618_scaler_configure);
static void rk618_scaler_bridge_enable(struct drm_bridge *bridge)
{
struct rk618_scaler *scl = bridge_to_scaler(bridge);
struct drm_connector *connector;
const struct drm_display_mode *src = &scl->mode;
const struct drm_display_mode *mode;
struct drm_display_mode dst;
unsigned long dclk_rate = src->clock * 1000;
u64 sclk_rate;
long rate;
memset(&dst, 0, sizeof(dst));
drm_for_each_connector(connector, bridge->dev) {
if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)
continue;
if (connector->encoder_ids[0] != bridge->encoder->base.id)
continue;
list_for_each_entry(mode, &connector->modes, head) {
if (mode->type & DRM_MODE_TYPE_PREFERRED) {
drm_mode_copy(&dst, mode);
break;
}
}
}
sclk_rate = (u64)dclk_rate * dst.vdisplay * dst.htotal;
do_div(sclk_rate, src->vdisplay * src->htotal);
dev_info(scl->dev, "src=%s, dst=%s\n", src->name, dst.name);
dev_info(scl->dev, "dclk rate: %ld, sclk rate: %lld\n",
dclk_rate, sclk_rate);
clk_set_parent(scl->dither_clk, scl->scaler_clk);
rate = clk_round_rate(scl->scaler_clk, sclk_rate);
clk_set_rate(scl->scaler_clk, rate);
dst.clock = rate / 1000;
clk_prepare_enable(scl->scaler_clk);
rk618_scaler_init(scl, src, &dst);
rk618_scaler_enable(scl);
}
static void rk618_scaler_bridge_disable(struct drm_bridge *bridge)
{
struct rk618_scaler *scl = bridge_to_scaler(bridge);
rk618_scaler_disable(scl);
clk_disable_unprepare(scl->scaler_clk);
clk_set_parent(scl->dither_clk, scl->vif_clk);
}
static void rk618_scaler_bridge_mode_set(struct drm_bridge *bridge,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted)
{
struct rk618_scaler *scl = bridge_to_scaler(bridge);
drm_mode_copy(&scl->mode, adjusted);
}
static int rk618_scaler_bridge_attach(struct drm_bridge *bridge)
{
struct rk618_scaler *scl = bridge_to_scaler(bridge);
struct device *dev = scl->dev;
struct device_node *endpoint;
int ret;
endpoint = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
if (endpoint && of_device_is_available(endpoint)) {
struct device_node *remote;
remote = of_graph_get_remote_port_parent(endpoint);
of_node_put(endpoint);
if (!remote || !of_device_is_available(remote))
return -ENODEV;
scl->bridge = of_drm_find_bridge(remote);
of_node_put(remote);
if (!scl->bridge)
return -EPROBE_DEFER;
scl->bridge->encoder = bridge->encoder;
ret = drm_bridge_attach(bridge->dev, scl->bridge);
if (ret) {
dev_err(dev, "failed to attach bridge\n");
return ret;
}
bridge->next = scl->bridge;
}
return 0;
}
static const struct drm_bridge_funcs rk618_scaler_bridge_funcs = {
.enable = rk618_scaler_bridge_enable,
.disable = rk618_scaler_bridge_disable,
.mode_set = rk618_scaler_bridge_mode_set,
.attach = rk618_scaler_bridge_attach,
};
static int rk618_scaler_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rk618_scaler *scl;
int ret;
if (!of_device_is_available(dev->of_node))
return -ENODEV;
scl = devm_kzalloc(dev, sizeof(*scl), GFP_KERNEL);
if (!scl)
return -ENOMEM;
scl->dev = dev;
platform_set_drvdata(pdev, scl);
scl->regmap = dev_get_regmap(dev->parent, NULL);
if (!scl->regmap)
return -ENODEV;
scl->vif_clk = devm_clk_get(dev, "vif");
if (IS_ERR(scl->vif_clk)) {
ret = PTR_ERR(scl->vif_clk);
dev_err(dev, "failed to get vif clock: %d\n", ret);
return ret;
}
scl->dither_clk = devm_clk_get(dev, "dither");
if (IS_ERR(scl->dither_clk)) {
ret = PTR_ERR(scl->dither_clk);
dev_err(dev, "failed to get dither clock: %d\n", ret);
return ret;
}
scl->scaler_clk = devm_clk_get(dev, "scaler");
if (IS_ERR(scl->scaler_clk)) {
ret = PTR_ERR(scl->scaler_clk);
dev_err(dev, "failed to get scaler clock: %d\n", ret);
return ret;
}
scl->base.funcs = &rk618_scaler_bridge_funcs;
scl->base.of_node = dev->of_node;
ret = drm_bridge_add(&scl->base);
if (ret) {
dev_err(dev, "failed to add bridge\n");
return ret;
}
return 0;
}
static int rk618_scaler_remove(struct platform_device *pdev)
{
struct rk618_scaler *scl = platform_get_drvdata(pdev);
drm_bridge_remove(&scl->base);
return 0;
}
static const struct of_device_id rk618_scaler_of_match[] = {
{ .compatible = "rockchip,rk618-scaler", },
{},
};
MODULE_DEVICE_TABLE(of, rk618_scaler_of_match);
static struct platform_driver rk618_scaler_driver = {
.driver = {
.name = "rk618-scaler",
.of_match_table = of_match_ptr(rk618_scaler_of_match),
},
.probe = rk618_scaler_probe,
.remove = rk618_scaler_remove,
};
module_platform_driver(rk618_scaler_driver);
MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip RK618 SCALER driver");
MODULE_LICENSE("GPL v2");

View File

@@ -1,18 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017 Rockchip Electronics Co. Ltd.
*
* 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.
* Author: Wyon Bi <bivvy.bi@rock-chips.com>
*/
#include "rk618_output.h"
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/mfd/rk618.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <drm/drm_of.h>
#include <drm/drmP.h>
#include <video/videomode.h>
#define RK618_VIF0_REG0 0x0000
#define VIF_ENABLE HIWORD_UPDATE(1, 0, 0)
@@ -64,8 +68,8 @@ static void rk618_vif_disable(struct rk618_vif *vif)
regmap_write(vif->regmap, RK618_VIF0_REG0, VIF_DISABLE);
}
static void rk618_vif_configure(struct rk618_vif *vif,
const struct drm_display_mode *mode)
static void rk618_vif_init(struct rk618_vif *vif,
const struct drm_display_mode *mode)
{
struct videomode vm;
u32 vif_frame_vst, vif_frame_hst;
@@ -74,9 +78,22 @@ static void rk618_vif_configure(struct rk618_vif *vif,
drm_display_mode_to_videomode(mode, &vm);
/* XXX */
vif_frame_vst = 1;
vif_frame_hst = 207;
if (!strcmp(mode->name, "1920x1080")) {
vif_frame_vst = 0x001;
vif_frame_hst = 0x0cb;
} else if (!strcmp(mode->name, "1600x900")) {
vif_frame_vst = 0x001;
vif_frame_hst = 0x327;
} else if (!strcmp(mode->name, "1280x720")) {
vif_frame_vst = 0x001;
vif_frame_hst = 0x0cf;
} else {
vif_frame_vst = 0x001;
vif_frame_hst = 0x001;
}
dev_dbg(vif->dev, "vif_frame_vst=%d, vif_frame_hst=%d\n",
vif_frame_vst, vif_frame_hst);
vif_hs_end = vm.hsync_len;
vif_htotal = vm.hsync_len + vm.hback_porch + vm.hfront_porch +
@@ -104,7 +121,7 @@ static void rk618_vif_configure(struct rk618_vif *vif,
VIF0_SYNC_MODE_ENABLE);
}
static void rk618_vif_bridge_pre_enable(struct drm_bridge *bridge)
static void rk618_vif_bridge_enable(struct drm_bridge *bridge)
{
struct rk618_vif *vif = bridge_to_vif(bridge);
const struct drm_display_mode *mode = &vif->mode;
@@ -114,16 +131,10 @@ static void rk618_vif_bridge_pre_enable(struct drm_bridge *bridge)
rate = clk_round_rate(vif->vif_clk, mode->clock * 1000);
clk_set_rate(vif->vif_clk, rate);
rk618_vif_configure(vif, mode);
}
static void rk618_vif_bridge_enable(struct drm_bridge *bridge)
{
struct rk618_vif *vif = bridge_to_vif(bridge);
rk618_vif_enable(vif);
clk_prepare_enable(vif->vif_clk);
rk618_vif_init(vif, mode);
rk618_vif_enable(vif);
}
static void rk618_vif_bridge_disable(struct drm_bridge *bridge)
@@ -131,12 +142,6 @@ static void rk618_vif_bridge_disable(struct drm_bridge *bridge)
struct rk618_vif *vif = bridge_to_vif(bridge);
rk618_vif_disable(vif);
}
static void rk618_vif_bridge_post_disable(struct drm_bridge *bridge)
{
struct rk618_vif *vif = bridge_to_vif(bridge);
clk_disable_unprepare(vif->vif_clk);
}
@@ -185,10 +190,8 @@ static int rk618_vif_bridge_attach(struct drm_bridge *bridge)
}
static const struct drm_bridge_funcs rk618_vif_bridge_funcs = {
.pre_enable = rk618_vif_bridge_pre_enable,
.enable = rk618_vif_bridge_enable,
.disable = rk618_vif_bridge_disable,
.post_disable = rk618_vif_bridge_post_disable,
.mode_set = rk618_vif_bridge_mode_set,
.attach = rk618_vif_bridge_attach,
};
@@ -209,7 +212,7 @@ static int rk618_vif_probe(struct platform_device *pdev)
vif->dev = dev;
platform_set_drvdata(pdev, vif);
vif->regmap = dev_get_regmap(pdev->dev.parent, NULL);
vif->regmap = dev_get_regmap(dev->parent, NULL);
if (!vif->regmap)
return -ENODEV;