Some DPI -> LVDS encoders might support several input bus width. Add a DT property to describe the bus width used on a specific design. v10: * Add changelog to the commit message v8 -> v9: * No changes v7: * Fix a use-after-release problem * Get rid of the data-mapping parsing * Use kmalloc instead of kcalloc. v4 -> v6: * Not part of the series v3: * Use bus-width for the rgb24/rgb18 distinction * Adjust code to match core changes * Get rid of of_get_data_mapping() * Don't implement ->atomic_check() (the core now takes care of bus flags propagation) v2: * Make the bus-format negotiation logic more generic Signed-off-by: Boris Brezillon <boris.brezillon@xxxxxxxxxxxxx> --- drivers/gpu/drm/bridge/lvds-codec.c | 64 ++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index 5f04cc11227e..f4fd8472c335 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -11,6 +11,7 @@ #include <linux/of_graph.h> #include <linux/platform_device.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_panel.h> @@ -19,6 +20,7 @@ struct lvds_codec { struct drm_bridge *panel_bridge; struct gpio_desc *powerdown_gpio; u32 connector_type; + u32 input_fmt; }; static int lvds_codec_attach(struct drm_bridge *bridge) @@ -48,18 +50,47 @@ static void lvds_codec_disable(struct drm_bridge *bridge) gpiod_set_value_cansleep(lvds_codec->powerdown_gpio, 1); } +static u32 * +lvds_codec_atomic_get_input_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + u32 output_fmt, + unsigned int *num_input_fmts) +{ + struct lvds_codec *lvds_codec = container_of(bridge, + struct lvds_codec, bridge); + u32 *input_fmts; + + input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL); + if (!input_fmts) { + *num_input_fmts = 0; + return NULL; + } + + *num_input_fmts = 1; + input_fmts[0] = lvds_codec->input_fmt; + return input_fmts; +} + static struct drm_bridge_funcs funcs = { .attach = lvds_codec_attach, .enable = lvds_codec_enable, .disable = lvds_codec_disable, + .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = lvds_codec_atomic_get_input_bus_fmts, }; static int lvds_codec_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *panel_node; + struct device_node *np; struct drm_panel *panel; struct lvds_codec *lvds_codec; + u32 input_bus_width; + int err; lvds_codec = devm_kzalloc(dev, sizeof(*lvds_codec), GFP_KERNEL); if (!lvds_codec) @@ -69,22 +100,43 @@ static int lvds_codec_probe(struct platform_device *pdev) lvds_codec->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); if (IS_ERR(lvds_codec->powerdown_gpio)) { - int err = PTR_ERR(lvds_codec->powerdown_gpio); + err = PTR_ERR(lvds_codec->powerdown_gpio); if (err != -EPROBE_DEFER) dev_err(dev, "powerdown GPIO failure: %d\n", err); return err; } + np = of_graph_get_port_by_id(dev->of_node, 0); + if (!np) { + dev_dbg(dev, "port 0 not found\n"); + return -ENXIO; + } + + err = of_property_read_u32(np, "bus-width", &input_bus_width); + of_node_put(np); + + if (err) { + lvds_codec->input_fmt = MEDIA_BUS_FMT_FIXED; + } else if (input_bus_width == 18) { + lvds_codec->input_fmt = MEDIA_BUS_FMT_RGB666_1X18; + } else if (input_bus_width == 24) { + lvds_codec->input_fmt = MEDIA_BUS_FMT_RGB888_1X24; + } else { + dev_dbg(dev, "unsupported bus-width value %u on port 0\n", + input_bus_width); + return -ENOTSUPP; + } + /* Locate the panel DT node. */ - panel_node = of_graph_get_remote_node(dev->of_node, 1, 0); - if (!panel_node) { + np = of_graph_get_remote_node(dev->of_node, 1, 0); + if (!np) { dev_dbg(dev, "panel DT node not found\n"); return -ENXIO; } - panel = of_drm_find_panel(panel_node); - of_node_put(panel_node); + panel = of_drm_find_panel(np); + of_node_put(np); if (IS_ERR(panel)) { dev_dbg(dev, "panel not found, deferring probe\n"); return PTR_ERR(panel); -- 2.24.1