Skip to content

Commit

Permalink
drm/rockchip: Add support for RGB output interface
Browse files Browse the repository at this point in the history
Some Rockchip CRTCs, like rv1108 and px30, can directly output parallel
and serial RGB data to panel or conversion chip.

So add a feature-bit for vops to mark the ability for these direct outputs
and add an internal encoder in that case, that can attach to bridge chips
or panels.

Signed-off-by: Sandy Huang <hjc@rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
  • Loading branch information
sandy-huang authored and mmind committed Aug 27, 2018
1 parent c5a48c7 commit fe74d2c
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 0 deletions.
11 changes: 11 additions & 0 deletions drivers/gpu/drm/rockchip/Kconfig
Expand Up @@ -8,6 +8,7 @@ config DRM_ROCKCHIP
select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
select DRM_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
select DRM_RGB if ROCKCHIP_RGB
select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC
help
Choose this option if you have a Rockchip soc chipset.
Expand Down Expand Up @@ -66,4 +67,14 @@ config ROCKCHIP_LVDS
Rockchip rk3288 SoC has LVDS TX Controller can be used, and it
support LVDS, rgb, dual LVDS output mode. say Y to enable its
driver.

config ROCKCHIP_RGB
bool "Rockchip RGB support"
depends on DRM_ROCKCHIP
depends on PINCTRL
help
Choose this option to enable support for Rockchip RGB output.
Some Rockchip CRTCs, like rv1108, can directly output parallel
and serial RGB format to panel or connect to a conversion chip.
say Y to enable its driver.
endif
1 change: 1 addition & 0 deletions drivers/gpu/drm/rockchip/Makefile
Expand Up @@ -14,5 +14,6 @@ rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o

obj-$(CONFIG_DRM_ROCKCHIP) += rockchipdrm.o
16 changes: 16 additions & 0 deletions drivers/gpu/drm/rockchip/rockchip_drm_vop.c
Expand Up @@ -41,6 +41,7 @@
#include "rockchip_drm_fb.h"
#include "rockchip_drm_psr.h"
#include "rockchip_drm_vop.h"
#include "rockchip_rgb.h"

#define VOP_WIN_SET(x, win, name, v) \
vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name)
Expand Down Expand Up @@ -92,6 +93,7 @@ struct vop_win {
struct vop *vop;
};

struct rockchip_rgb;
struct vop {
struct drm_crtc crtc;
struct device *dev;
Expand Down Expand Up @@ -135,6 +137,9 @@ struct vop {
/* vop dclk reset */
struct reset_control *dclk_rst;

/* optional internal rgb encoder */
struct rockchip_rgb *rgb;

struct vop_win win[];
};

Expand Down Expand Up @@ -1638,6 +1643,14 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
if (ret)
goto err_disable_pm_runtime;

if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) {
vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev);
if (IS_ERR(vop->rgb)) {
ret = PTR_ERR(vop->rgb);
goto err_disable_pm_runtime;
}
}

return 0;

err_disable_pm_runtime:
Expand All @@ -1650,6 +1663,9 @@ static void vop_unbind(struct device *dev, struct device *master, void *data)
{
struct vop *vop = dev_get_drvdata(dev);

if (vop->rgb)
rockchip_rgb_fini(vop->rgb);

pm_runtime_disable(dev);
vop_destroy_crtc(vop);

Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/rockchip/rockchip_drm_vop.h
Expand Up @@ -162,6 +162,7 @@ struct vop_data {
unsigned int win_size;

#define VOP_FEATURE_OUTPUT_RGB10 BIT(0)
#define VOP_FEATURE_INTERNAL_RGB BIT(1)
u64 feature;
};

Expand Down
212 changes: 212 additions & 0 deletions drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -0,0 +1,212 @@
/*
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
* Author:
* Sandy Huang <hjc@rock-chips.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/

#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_panel.h>
#include <drm/drm_of.h>

#include <linux/component.h>
#include <linux/of_graph.h>

#include "rockchip_drm_drv.h"
#include "rockchip_drm_vop.h"

#define encoder_to_rgb(c) container_of(c, struct rockchip_rgb, encoder)

struct rockchip_rgb {
struct device *dev;
struct drm_device *drm_dev;
struct drm_bridge *bridge;
struct drm_encoder encoder;
// struct dev_pin_info *pins;
int output_mode;
};

static inline int name_to_output_mode(const char *s)
{
static const struct {
const char *name;
int format;
} formats[] = {
{ "p888", ROCKCHIP_OUT_MODE_P888 },
{ "p666", ROCKCHIP_OUT_MODE_P666 },
{ "p565", ROCKCHIP_OUT_MODE_P565 }
};
int i;

for (i = 0; i < ARRAY_SIZE(formats); i++)
if (!strncmp(s, formats[i].name, strlen(formats[i].name)))
return formats[i].format;

return -EINVAL;
}

static void rockchip_rgb_encoder_enable(struct drm_encoder *encoder)
{
struct rockchip_rgb *rgb = encoder_to_rgb(encoder);

// if (rgb->pins && !IS_ERR(rgb->pins->default_state))
// pinctrl_select_state(rgb->pins->p, rgb->pins->default_state);
}

static void rockchip_rgb_encoder_disable(struct drm_encoder *encoder)
{
struct rockchip_rgb *rgb = encoder_to_rgb(encoder);

//FIXME: pin-voodoo?
}

static int
rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct rockchip_rgb *rgb = encoder_to_rgb(encoder);

s->output_mode = rgb->output_mode;
s->output_type = DRM_MODE_CONNECTOR_LVDS;

return 0;
}

static const
struct drm_encoder_helper_funcs rockchip_rgb_encoder_helper_funcs = {
.enable = rockchip_rgb_encoder_enable,
.disable = rockchip_rgb_encoder_disable,
.atomic_check = rockchip_rgb_encoder_atomic_check,
};

static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};

struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
struct drm_crtc *crtc,
struct drm_device *drm_dev)
{
struct rockchip_rgb *rgb;
struct drm_encoder *encoder;
struct device_node *port, *endpoint;
u32 endpoint_id;
int ret = 0, child_count = 0;
struct drm_panel *panel;
struct drm_bridge *bridge;

rgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL);
if (!rgb)
return ERR_PTR(-ENOMEM);

rgb->dev = dev;
rgb->drm_dev = drm_dev;

/* rgb->pins = devm_kzalloc(rgb->dev, sizeof(*rgb->pins), GFP_KERNEL);
if (!rgb->pins)
return -ENOMEM; */

/* rgb->pins->p = devm_pinctrl_get(rgb->dev);
if (IS_ERR(rgb->pins->p)) {
DRM_DEV_ERROR(dev, "no pinctrl handle\n");
devm_kfree(rgb->dev, rgb->pins);
rgb->pins = NULL;
} else {
rgb->pins->default_state =
pinctrl_lookup_state(rgb->pins->p, "lcdc");
if (IS_ERR(rgb->pins->default_state)) {
DRM_DEV_ERROR(dev, "no default pinctrl state\n");
devm_kfree(rgb->dev, rgb->pins);
rgb->pins = NULL;
}
} */

port = of_graph_get_port_by_id(dev->of_node, 0);
if (!port)
return ERR_PTR(-EINVAL);

for_each_child_of_node(port, endpoint) {
if (of_property_read_u32(endpoint, "reg", &endpoint_id))
endpoint_id = 0;

if (rockchip_drm_endpoint_is_subdriver(endpoint))
continue;

child_count++;
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, endpoint_id,
&panel, &bridge);
if (!ret)
break;
}

of_node_put(port);

/* if the rgb output is not connected to anything, just return */
if (!child_count)
return NULL;

if (ret < 0) {
if (ret != -EPROBE_DEFER)
DRM_DEV_ERROR(dev, "failed to find panel or bridge %d\n", ret);
return ERR_PTR(ret);
}

rgb->output_mode = ROCKCHIP_OUT_MODE_P888;

encoder = &rgb->encoder;
encoder->possible_crtcs = drm_crtc_mask(crtc);

ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL);
if (ret < 0) {
DRM_DEV_ERROR(drm_dev->dev,
"failed to initialize encoder: %d\n", ret);
return ERR_PTR(ret);
}

drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs);

if (panel) {
bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_LVDS);
if (IS_ERR(bridge))
return ERR_CAST(bridge);
}

rgb->bridge = bridge;

ret = drm_bridge_attach(encoder, rgb->bridge, NULL);
if (ret) {
DRM_DEV_ERROR(drm_dev->dev,
"failed to attach bridge: %d\n", ret);
goto err_free_encoder;
}

return rgb;

err_free_encoder:
drm_encoder_cleanup(encoder);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(rockchip_rgb_init);

void rockchip_rgb_fini(struct rockchip_rgb *rgb)
{
rockchip_rgb_encoder_disable(&rgb->encoder);

drm_panel_bridge_remove(rgb->bridge);
drm_encoder_cleanup(&rgb->encoder);
}
EXPORT_SYMBOL_GPL(rockchip_rgb_fini);
19 changes: 19 additions & 0 deletions drivers/gpu/drm/rockchip/rockchip_rgb.h
@@ -0,0 +1,19 @@
/*
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
* Author:
* Sandy Huang <hjc@rock-chips.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/

struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
struct drm_crtc *crtc,
struct drm_device *drm_dev);
void rockchip_rgb_fini(struct rockchip_rgb *rgb);
1 change: 1 addition & 0 deletions drivers/gpu/drm/rockchip/rockchip_vop_reg.c
Expand Up @@ -487,6 +487,7 @@ static const struct vop_data rk3188_vop = {
.output = &rk3188_output,
.win = rk3188_vop_win_data,
.win_size = ARRAY_SIZE(rk3188_vop_win_data),
.feature = VOP_FEATURE_INTERNAL_RGB,
};

static const struct vop_scl_extension rk3288_win_full_scl_ext = {
Expand Down

0 comments on commit fe74d2c

Please sign in to comment.