mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-16 23:45:31 +08:00
R-Car DU changes for v5.3:
- R8A774A1 SoC support - LVDS dual-link mode support - Support for additional formats - Misc fixes -----BEGIN PGP SIGNATURE----- iQJWBAABCgBAFiEEvZRkio5H7O2/GZsYYiVdKZ4oCyQFAlz7uhsiHGxhdXJlbnQu cGluY2hhcnRAaWRlYXNvbmJvYXJkLmNvbQAKCRBiJV0pnigLJEXWD/9Q92VnI5cg ITqmrxsN8O9ArFBoKyb6UOyUWaz7QsQeQZw78oSxT7vKtBbBK8AYlcjmuxBZPZqe lu1PVcdd5/C+wU4Izp+TMbT2rKDSP2rK4fk5T40uYfkxeFvYAyBA+duRbnVjvWRW n34bFuI+lfsW1LjBjVzCTG6aWfiR4/gcZTdSSH4+iL+k39nnu+WE5JdJWI54s8Fy X1W7szMASf3/psj6/6kAhEcvfo9D+p/EPjOw/mACGsUdZ1o9Fj4abCBHNRGEN7Dx jypZPTRU/V3z8/YrEQfAezWlFUouCyEwL1JMnylfJpdAzncwh1y7lk7vc52EmE+l gKL0BYl04A7u3prXAtjYZXFNoXaDFQ2MqguZ73LLQ1izSdJxQJK6bGEtwssuSn/p AUHCp9Je/E+g4XObCFToXgLzR4WK08yXmIGkNACYQEB3hNtyEenA4YKja4GadI+l wz4/iYWE/1/jhUb9oWVAJuc34+bmv18vD4r3poLZWjtWjTkOZLJpmtte2xScBkbi FbQL7zncc/0KbkA8ELoKDIWx07vjTBAx2Xcl1pXbbt4efBg3b1jxCI+2zJwqmbQJ AdGli53PmfkzJBcM9D7ETnbC+/LHCF1i4Af9yf2zVpLUJ8o9j1gQvRPSqf5Rnj0v Cfger7hNS75MYGuB26Y4K/mQ0bb60Hm2XQ== =VoV+ -----END PGP SIGNATURE----- Merge tag 'du-next-20190608-2' of git://linuxtv.org/pinchartl/media into drm-next R-Car DU changes for v5.3: - R8A774A1 SoC support - LVDS dual-link mode support - Support for additional formats - Misc fixes Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190608134652.GE4786@pendragon.ideasonboard.com
This commit is contained in:
commit
34c8a892ec
@ -9,6 +9,7 @@ Required properties:
|
||||
- compatible : Shall contain one of
|
||||
- "renesas,r8a7743-lvds" for R8A7743 (RZ/G1M) compatible LVDS encoders
|
||||
- "renesas,r8a7744-lvds" for R8A7744 (RZ/G1N) compatible LVDS encoders
|
||||
- "renesas,r8a774a1-lvds" for R8A774A1 (RZ/G2M) compatible LVDS encoders
|
||||
- "renesas,r8a774c0-lvds" for R8A774C0 (RZ/G2E) compatible LVDS encoders
|
||||
- "renesas,r8a7790-lvds" for R8A7790 (R-Car H2) compatible LVDS encoders
|
||||
- "renesas,r8a7791-lvds" for R8A7791 (R-Car M2-W) compatible LVDS encoders
|
||||
@ -45,14 +46,24 @@ OF graph bindings specified in Documentation/devicetree/bindings/graph.txt.
|
||||
|
||||
Each port shall have a single endpoint.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- renesas,companion : phandle to the companion LVDS encoder. This property is
|
||||
mandatory for the first LVDS encoder on D3 and E3 SoCs, and shall point to
|
||||
the second encoder to be used as a companion in dual-link mode. It shall not
|
||||
be set for any other LVDS encoder.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
lvds0: lvds@feb90000 {
|
||||
compatible = "renesas,r8a7790-lvds";
|
||||
reg = <0 0xfeb90000 0 0x1c>;
|
||||
clocks = <&cpg CPG_MOD 726>;
|
||||
resets = <&cpg 726>;
|
||||
compatible = "renesas,r8a77990-lvds";
|
||||
reg = <0 0xfeb90000 0 0x20>;
|
||||
clocks = <&cpg CPG_MOD 727>;
|
||||
power-domains = <&sysc R8A77990_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 727>;
|
||||
|
||||
renesas,companion = <&lvds1>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
|
@ -28,6 +28,12 @@ Optional video port nodes:
|
||||
- port@1: Second LVDS input port
|
||||
- port@3: Second digital CMOS/TTL parallel output
|
||||
|
||||
The device can operate in single-link mode or dual-link mode. In single-link
|
||||
mode, all pixels are received on port@0, and port@1 shall not contain any
|
||||
endpoint. In dual-link mode, even-numbered pixels are received on port@0 and
|
||||
odd-numbered pixels on port@1, and both port@0 and port@1 shall contain
|
||||
endpoints.
|
||||
|
||||
Example:
|
||||
--------
|
||||
|
||||
|
@ -7,6 +7,7 @@ Required Properties:
|
||||
- "renesas,du-r8a7744" for R8A7744 (RZ/G1N) compatible DU
|
||||
- "renesas,du-r8a7745" for R8A7745 (RZ/G1E) compatible DU
|
||||
- "renesas,du-r8a77470" for R8A77470 (RZ/G1C) compatible DU
|
||||
- "renesas,du-r8a774a1" for R8A774A1 (RZ/G2M) compatible DU
|
||||
- "renesas,du-r8a774c0" for R8A774C0 (RZ/G2E) compatible DU
|
||||
- "renesas,du-r8a7779" for R8A7779 (R-Car H1) compatible DU
|
||||
- "renesas,du-r8a7790" for R8A7790 (R-Car H2) compatible DU
|
||||
@ -58,6 +59,7 @@ corresponding to each DU output.
|
||||
R8A7744 (RZ/G1N) DPAD 0 LVDS 0 - -
|
||||
R8A7745 (RZ/G1E) DPAD 0 DPAD 1 - -
|
||||
R8A77470 (RZ/G1C) DPAD 0 DPAD 1 LVDS 0 -
|
||||
R8A774A1 (RZ/G2M) DPAD 0 HDMI 0 LVDS 0 -
|
||||
R8A774C0 (RZ/G2E) DPAD 0 LVDS 0 LVDS 1 -
|
||||
R8A7779 (R-Car H1) DPAD 0 DPAD 1 - -
|
||||
R8A7790 (R-Car H2) DPAD 0 LVDS 0 LVDS 1 -
|
||||
|
@ -33,6 +33,8 @@ struct thc63_dev {
|
||||
|
||||
struct drm_bridge bridge;
|
||||
struct drm_bridge *next;
|
||||
|
||||
struct drm_bridge_timings timings;
|
||||
};
|
||||
|
||||
static inline struct thc63_dev *to_thc63(struct drm_bridge *bridge)
|
||||
@ -50,15 +52,28 @@ static int thc63_attach(struct drm_bridge *bridge)
|
||||
static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct thc63_dev *thc63 = to_thc63(bridge);
|
||||
unsigned int min_freq;
|
||||
unsigned int max_freq;
|
||||
|
||||
/*
|
||||
* The THC63LVD1024 clock frequency range is 8 to 135 MHz in single-in
|
||||
* mode. Note that the limits are different in dual-in, single-out mode,
|
||||
* and will need to be adjusted accordingly.
|
||||
* The THC63LVD1024 pixel rate range is 8 to 135 MHz in all modes but
|
||||
* dual-in, single-out where it is 40 to 150 MHz. As dual-in, dual-out
|
||||
* isn't supported by the driver yet, simply derive the limits from the
|
||||
* input mode.
|
||||
*/
|
||||
if (mode->clock < 8000)
|
||||
if (thc63->timings.dual_link) {
|
||||
min_freq = 40000;
|
||||
max_freq = 150000;
|
||||
} else {
|
||||
min_freq = 8000;
|
||||
max_freq = 135000;
|
||||
}
|
||||
|
||||
if (mode->clock < min_freq)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
if (mode->clock > 135000)
|
||||
if (mode->clock > max_freq)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
return MODE_OK;
|
||||
@ -103,19 +118,19 @@ static const struct drm_bridge_funcs thc63_bridge_func = {
|
||||
|
||||
static int thc63_parse_dt(struct thc63_dev *thc63)
|
||||
{
|
||||
struct device_node *thc63_out;
|
||||
struct device_node *endpoint;
|
||||
struct device_node *remote;
|
||||
|
||||
thc63_out = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
|
||||
THC63_RGB_OUT0, -1);
|
||||
if (!thc63_out) {
|
||||
endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
|
||||
THC63_RGB_OUT0, -1);
|
||||
if (!endpoint) {
|
||||
dev_err(thc63->dev, "Missing endpoint in port@%u\n",
|
||||
THC63_RGB_OUT0);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
remote = of_graph_get_remote_port_parent(thc63_out);
|
||||
of_node_put(thc63_out);
|
||||
remote = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
if (!remote) {
|
||||
dev_err(thc63->dev, "Endpoint in port@%u unconnected\n",
|
||||
THC63_RGB_OUT0);
|
||||
@ -134,6 +149,22 @@ static int thc63_parse_dt(struct thc63_dev *thc63)
|
||||
if (!thc63->next)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
endpoint = of_graph_get_endpoint_by_regs(thc63->dev->of_node,
|
||||
THC63_LVDS_IN1, -1);
|
||||
if (endpoint) {
|
||||
remote = of_graph_get_remote_port_parent(endpoint);
|
||||
of_node_put(endpoint);
|
||||
|
||||
if (remote) {
|
||||
if (of_device_is_available(remote))
|
||||
thc63->timings.dual_link = true;
|
||||
of_node_put(remote);
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(thc63->dev, "operating in %s-link mode\n",
|
||||
thc63->timings.dual_link ? "dual" : "single");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -190,6 +221,7 @@ static int thc63_probe(struct platform_device *pdev)
|
||||
thc63->bridge.driver_private = thc63;
|
||||
thc63->bridge.of_node = pdev->dev.of_node;
|
||||
thc63->bridge.funcs = &thc63_bridge_func;
|
||||
thc63->bridge.timings = &thc63->timings;
|
||||
|
||||
drm_bridge_add(&thc63->bridge);
|
||||
|
||||
|
@ -102,6 +102,35 @@ static const struct rcar_du_device_info rzg1_du_r8a77470_info = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rcar_du_device_info rcar_du_r8a774a1_info = {
|
||||
.gen = 3,
|
||||
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
|
||||
| RCAR_DU_FEATURE_VSP1_SOURCE
|
||||
| RCAR_DU_FEATURE_INTERLACED
|
||||
| RCAR_DU_FEATURE_TVM_SYNC,
|
||||
.channels_mask = BIT(2) | BIT(1) | BIT(0),
|
||||
.routes = {
|
||||
/*
|
||||
* R8A774A1 has one RGB output, one LVDS output and one HDMI
|
||||
* output.
|
||||
*/
|
||||
[RCAR_DU_OUTPUT_DPAD0] = {
|
||||
.possible_crtcs = BIT(2),
|
||||
.port = 0,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_HDMI0] = {
|
||||
.possible_crtcs = BIT(1),
|
||||
.port = 1,
|
||||
},
|
||||
[RCAR_DU_OUTPUT_LVDS0] = {
|
||||
.possible_crtcs = BIT(0),
|
||||
.port = 2,
|
||||
},
|
||||
},
|
||||
.num_lvds = 1,
|
||||
.dpll_mask = BIT(1),
|
||||
};
|
||||
|
||||
static const struct rcar_du_device_info rcar_du_r8a774c0_info = {
|
||||
.gen = 3,
|
||||
.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
|
||||
@ -386,6 +415,7 @@ static const struct of_device_id rcar_du_of_table[] = {
|
||||
{ .compatible = "renesas,du-r8a7744", .data = &rzg1_du_r8a7743_info },
|
||||
{ .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info },
|
||||
{ .compatible = "renesas,du-r8a77470", .data = &rzg1_du_r8a77470_info },
|
||||
{ .compatible = "renesas,du-r8a774a1", .data = &rcar_du_r8a774a1_info },
|
||||
{ .compatible = "renesas,du-r8a774c0", .data = &rcar_du_r8a774c0_info },
|
||||
{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
|
||||
{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_encoder.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_lvds.h"
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Encoder
|
||||
@ -97,6 +98,17 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* On Gen3 skip the LVDS1 output if the LVDS1 encoder is used as a
|
||||
* companion for LVDS0 in dual-link mode.
|
||||
*/
|
||||
if (rcdu->info->gen >= 3 && output == RCAR_DU_OUTPUT_LVDS1) {
|
||||
if (rcar_lvds_dual_link(bridge)) {
|
||||
ret = -ENOLINK;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
|
||||
DRM_MODE_ENCODER_NONE, NULL);
|
||||
if (ret < 0)
|
||||
|
@ -123,6 +123,66 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
|
||||
.v4l2 = V4L2_PIX_FMT_XRGB444,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_RGBA4444,
|
||||
.v4l2 = V4L2_PIX_FMT_RGBA444,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_RGBX4444,
|
||||
.v4l2 = V4L2_PIX_FMT_RGBX444,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_ABGR4444,
|
||||
.v4l2 = V4L2_PIX_FMT_ABGR444,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_XBGR4444,
|
||||
.v4l2 = V4L2_PIX_FMT_XBGR444,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_BGRA4444,
|
||||
.v4l2 = V4L2_PIX_FMT_BGRA444,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_BGRX4444,
|
||||
.v4l2 = V4L2_PIX_FMT_BGRX444,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_RGBA5551,
|
||||
.v4l2 = V4L2_PIX_FMT_RGBA555,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_RGBX5551,
|
||||
.v4l2 = V4L2_PIX_FMT_RGBX555,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_ABGR1555,
|
||||
.v4l2 = V4L2_PIX_FMT_ABGR555,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_XBGR1555,
|
||||
.v4l2 = V4L2_PIX_FMT_XBGR555,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_BGRA5551,
|
||||
.v4l2 = V4L2_PIX_FMT_BGRA555,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_BGRX5551,
|
||||
.v4l2 = V4L2_PIX_FMT_BGRX555,
|
||||
.bpp = 16,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_BGR888,
|
||||
.v4l2 = V4L2_PIX_FMT_RGB24,
|
||||
@ -133,6 +193,26 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = {
|
||||
.v4l2 = V4L2_PIX_FMT_BGR24,
|
||||
.bpp = 24,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_RGBA8888,
|
||||
.v4l2 = V4L2_PIX_FMT_BGRA32,
|
||||
.bpp = 32,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_RGBX8888,
|
||||
.v4l2 = V4L2_PIX_FMT_BGRX32,
|
||||
.bpp = 32,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_ABGR8888,
|
||||
.v4l2 = V4L2_PIX_FMT_RGBA32,
|
||||
.bpp = 32,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_XBGR8888,
|
||||
.v4l2 = V4L2_PIX_FMT_RGBX32,
|
||||
.bpp = 32,
|
||||
.planes = 1,
|
||||
}, {
|
||||
.fourcc = DRM_FORMAT_BGRA8888,
|
||||
.v4l2 = V4L2_PIX_FMT_ARGB32,
|
||||
@ -378,7 +458,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
|
||||
}
|
||||
|
||||
ret = rcar_du_encoder_init(rcdu, output, entity);
|
||||
if (ret && ret != -EPROBE_DEFER)
|
||||
if (ret && ret != -EPROBE_DEFER && ret != -ENOLINK)
|
||||
dev_warn(rcdu->dev,
|
||||
"failed to initialize encoder %pOF on output %u (%d), skipping\n",
|
||||
entity, output, ret);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "rcar_du_crtc.h"
|
||||
#include "rcar_du_drv.h"
|
||||
#include "rcar_du_kms.h"
|
||||
#include "rcar_du_writeback.h"
|
||||
|
||||
/**
|
||||
* struct rcar_du_wb_conn_state - Driver-specific writeback connector state
|
||||
|
@ -63,10 +63,12 @@ struct rcar_lvds {
|
||||
struct clk *extal; /* External clock */
|
||||
struct clk *dotclkin[2]; /* External DU clocks */
|
||||
} clocks;
|
||||
bool enabled;
|
||||
|
||||
struct drm_display_mode display_mode;
|
||||
enum rcar_lvds_mode mode;
|
||||
|
||||
struct drm_bridge *companion;
|
||||
bool dual_link;
|
||||
};
|
||||
|
||||
#define bridge_to_rcar_lvds(bridge) \
|
||||
@ -368,15 +370,12 @@ int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq)
|
||||
|
||||
dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq);
|
||||
|
||||
WARN_ON(lvds->enabled);
|
||||
|
||||
ret = clk_prepare_enable(lvds->clocks.mod);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
__rcar_lvds_pll_setup_d3_e3(lvds, freq, true);
|
||||
|
||||
lvds->enabled = true;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcar_lvds_clk_enable);
|
||||
@ -390,13 +389,9 @@ void rcar_lvds_clk_disable(struct drm_bridge *bridge)
|
||||
|
||||
dev_dbg(lvds->dev, "disabling LVDS PLL\n");
|
||||
|
||||
WARN_ON(!lvds->enabled);
|
||||
|
||||
rcar_lvds_write(lvds, LVDPLLCR, 0);
|
||||
|
||||
clk_disable_unprepare(lvds->clocks.mod);
|
||||
|
||||
lvds->enabled = false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcar_lvds_clk_disable);
|
||||
|
||||
@ -408,21 +403,18 @@ static void rcar_lvds_enable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
|
||||
const struct drm_display_mode *mode = &lvds->display_mode;
|
||||
/*
|
||||
* FIXME: We should really retrieve the CRTC through the state, but how
|
||||
* do we get a state pointer?
|
||||
*/
|
||||
struct drm_crtc *crtc = lvds->bridge.encoder->crtc;
|
||||
u32 lvdhcr;
|
||||
u32 lvdcr0;
|
||||
int ret;
|
||||
|
||||
WARN_ON(lvds->enabled);
|
||||
|
||||
ret = clk_prepare_enable(lvds->clocks.mod);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
/* Enable the companion LVDS encoder in dual-link mode. */
|
||||
if (lvds->dual_link && lvds->companion)
|
||||
lvds->companion->funcs->enable(lvds->companion);
|
||||
|
||||
/*
|
||||
* Hardcode the channels and control signals routing for now.
|
||||
*
|
||||
@ -445,17 +437,33 @@ static void rcar_lvds_enable(struct drm_bridge *bridge)
|
||||
rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
|
||||
|
||||
if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) {
|
||||
/* Disable dual-link mode. */
|
||||
rcar_lvds_write(lvds, LVDSTRIPE, 0);
|
||||
/*
|
||||
* Configure vertical stripe based on the mode of operation of
|
||||
* the connected device.
|
||||
*/
|
||||
rcar_lvds_write(lvds, LVDSTRIPE,
|
||||
lvds->dual_link ? LVDSTRIPE_ST_ON : 0);
|
||||
}
|
||||
|
||||
/* PLL clock configuration. */
|
||||
lvds->info->pll_setup(lvds, mode->clock * 1000);
|
||||
/*
|
||||
* PLL clock configuration on all instances but the companion in
|
||||
* dual-link mode.
|
||||
*/
|
||||
if (!lvds->dual_link || lvds->companion)
|
||||
lvds->info->pll_setup(lvds, mode->clock * 1000);
|
||||
|
||||
/* Set the LVDS mode and select the input. */
|
||||
lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT;
|
||||
if (drm_crtc_index(crtc) == 2)
|
||||
lvdcr0 |= LVDCR0_DUSEL;
|
||||
|
||||
if (lvds->bridge.encoder) {
|
||||
/*
|
||||
* FIXME: We should really retrieve the CRTC through the state,
|
||||
* but how do we get a state pointer?
|
||||
*/
|
||||
if (drm_crtc_index(lvds->bridge.encoder->crtc) == 2)
|
||||
lvdcr0 |= LVDCR0_DUSEL;
|
||||
}
|
||||
|
||||
rcar_lvds_write(lvds, LVDCR0, lvdcr0);
|
||||
|
||||
/* Turn all the channels on. */
|
||||
@ -507,16 +515,12 @@ static void rcar_lvds_enable(struct drm_bridge *bridge)
|
||||
drm_panel_prepare(lvds->panel);
|
||||
drm_panel_enable(lvds->panel);
|
||||
}
|
||||
|
||||
lvds->enabled = true;
|
||||
}
|
||||
|
||||
static void rcar_lvds_disable(struct drm_bridge *bridge)
|
||||
{
|
||||
struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
|
||||
|
||||
WARN_ON(!lvds->enabled);
|
||||
|
||||
if (lvds->panel) {
|
||||
drm_panel_disable(lvds->panel);
|
||||
drm_panel_unprepare(lvds->panel);
|
||||
@ -526,9 +530,11 @@ static void rcar_lvds_disable(struct drm_bridge *bridge)
|
||||
rcar_lvds_write(lvds, LVDCR1, 0);
|
||||
rcar_lvds_write(lvds, LVDPLLCR, 0);
|
||||
|
||||
clk_disable_unprepare(lvds->clocks.mod);
|
||||
/* Disable the companion LVDS encoder in dual-link mode. */
|
||||
if (lvds->dual_link && lvds->companion)
|
||||
lvds->companion->funcs->disable(lvds->companion);
|
||||
|
||||
lvds->enabled = false;
|
||||
clk_disable_unprepare(lvds->clocks.mod);
|
||||
}
|
||||
|
||||
static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge,
|
||||
@ -592,8 +598,6 @@ static void rcar_lvds_mode_set(struct drm_bridge *bridge,
|
||||
{
|
||||
struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
|
||||
|
||||
WARN_ON(lvds->enabled);
|
||||
|
||||
lvds->display_mode = *adjusted_mode;
|
||||
|
||||
rcar_lvds_get_lvds_mode(lvds);
|
||||
@ -646,10 +650,57 @@ static const struct drm_bridge_funcs rcar_lvds_bridge_ops = {
|
||||
.mode_set = rcar_lvds_mode_set,
|
||||
};
|
||||
|
||||
bool rcar_lvds_dual_link(struct drm_bridge *bridge)
|
||||
{
|
||||
struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
|
||||
|
||||
return lvds->dual_link;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcar_lvds_dual_link);
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Probe & Remove
|
||||
*/
|
||||
|
||||
static int rcar_lvds_parse_dt_companion(struct rcar_lvds *lvds)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
struct device_node *companion;
|
||||
struct device *dev = lvds->dev;
|
||||
int ret = 0;
|
||||
|
||||
/* Locate the companion LVDS encoder for dual-link operation, if any. */
|
||||
companion = of_parse_phandle(dev->of_node, "renesas,companion", 0);
|
||||
if (!companion) {
|
||||
dev_err(dev, "Companion LVDS encoder not found\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check: the companion encoder must have the same compatible
|
||||
* string.
|
||||
*/
|
||||
match = of_match_device(dev->driver->of_match_table, dev);
|
||||
if (!of_device_is_compatible(companion, match->compatible)) {
|
||||
dev_err(dev, "Companion LVDS encoder is invalid\n");
|
||||
ret = -ENXIO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
lvds->companion = of_drm_find_bridge(companion);
|
||||
if (!lvds->companion) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Found companion encoder %pOF\n", companion);
|
||||
|
||||
done:
|
||||
of_node_put(companion);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
|
||||
{
|
||||
struct device_node *local_output = NULL;
|
||||
@ -700,14 +751,26 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
|
||||
|
||||
if (is_bridge) {
|
||||
lvds->next_bridge = of_drm_find_bridge(remote);
|
||||
if (!lvds->next_bridge)
|
||||
if (!lvds->next_bridge) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK)
|
||||
lvds->dual_link = lvds->next_bridge->timings
|
||||
? lvds->next_bridge->timings->dual_link
|
||||
: false;
|
||||
} else {
|
||||
lvds->panel = of_drm_find_panel(remote);
|
||||
if (IS_ERR(lvds->panel))
|
||||
if (IS_ERR(lvds->panel)) {
|
||||
ret = PTR_ERR(lvds->panel);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (lvds->dual_link)
|
||||
ret = rcar_lvds_parse_dt_companion(lvds);
|
||||
|
||||
done:
|
||||
of_node_put(local_output);
|
||||
of_node_put(remote_input);
|
||||
@ -793,7 +856,6 @@ static int rcar_lvds_probe(struct platform_device *pdev)
|
||||
|
||||
lvds->dev = &pdev->dev;
|
||||
lvds->info = of_device_get_match_data(&pdev->dev);
|
||||
lvds->enabled = false;
|
||||
|
||||
ret = rcar_lvds_parse_dt(lvds);
|
||||
if (ret < 0)
|
||||
@ -866,6 +928,7 @@ static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = {
|
||||
static const struct of_device_id rcar_lvds_of_table[] = {
|
||||
{ .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info },
|
||||
{ .compatible = "renesas,r8a7744-lvds", .data = &rcar_lvds_gen2_info },
|
||||
{ .compatible = "renesas,r8a774a1-lvds", .data = &rcar_lvds_gen3_info },
|
||||
{ .compatible = "renesas,r8a774c0-lvds", .data = &rcar_lvds_r8a77990_info },
|
||||
{ .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_r8a7790_info },
|
||||
{ .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info },
|
||||
|
@ -15,6 +15,7 @@ struct drm_bridge;
|
||||
#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
|
||||
int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq);
|
||||
void rcar_lvds_clk_disable(struct drm_bridge *bridge);
|
||||
bool rcar_lvds_dual_link(struct drm_bridge *bridge);
|
||||
#else
|
||||
static inline int rcar_lvds_clk_enable(struct drm_bridge *bridge,
|
||||
unsigned long freq)
|
||||
@ -22,6 +23,10 @@ static inline int rcar_lvds_clk_enable(struct drm_bridge *bridge,
|
||||
return -ENOSYS;
|
||||
}
|
||||
static inline void rcar_lvds_clk_disable(struct drm_bridge *bridge) { }
|
||||
static inline bool rcar_lvds_dual_link(struct drm_bridge *bridge)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_DRM_RCAR_LVDS */
|
||||
|
||||
#endif /* __RCAR_LVDS_H__ */
|
||||
|
@ -265,6 +265,14 @@ struct drm_bridge_timings {
|
||||
* input signal after the clock edge.
|
||||
*/
|
||||
u32 hold_time_ps;
|
||||
/**
|
||||
* @dual_link:
|
||||
*
|
||||
* True if the bus operates in dual-link mode. The exact meaning is
|
||||
* dependent on the bus type. For LVDS buses, this indicates that even-
|
||||
* and odd-numbered pixels are received on separate links.
|
||||
*/
|
||||
bool dual_link;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user