This series is based on master branch of Linus tree at: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
I have tested this after adding few DT changes for exynos5250-snow and exynos5420-peach-pit boards.
The V4 series of this particular patchset was also tested by: Rahul Sharma rahul.sharma@samsung.com Javier Martinez Canillas javier@dowhile0.org
Changes since V2: -- Address comments from Jingoo Han for ps8622 driver -- Address comments from Daniel, Rob and Thierry regarding bridge chaining -- Address comments from Thierry regarding the names for new drm_panel functions
Changes since V3: -- Remove hotplug based initialization of exynos_dp -- Make exynos_dp work directly with drm_panel, remove dependency on panel_binder -- Minor cleanups in panel_binder and panel_lvds driver
Changes since V4: -- Use gpiod interface for panel-lvds and ps8622 drivers. -- Address comments from Javier. -- Fix compilation issues when PANEL_BINDER is selected as module. -- Split Documentation patches from driver patches. -- Rebase on top of the tree.
Changes since V5: -- Modify bridge drivers to support driver model. -- Drop the concept of bridge chain(sincle there are no 2 real bridges) Hence drop bridge-panel_binder layer. -- Drop panel-lvds driver and accomodate the required changes in panel-simple driver. -- Use gpiod interface in ptn3460 driver. -- Address all comments by Thierry Reding for V5 series. -- Address comments from Sean Paul for exynos_dp_commit issue.
Changes since V6: -- Panel patches were seperated and they are merged already. -- Fix few issues with ptn3460, before modifying the bridge core. -- Modify drm_bridge as per Thierry's comments for V6 series. -- Add drm_bridge changes minimally without breaking existing code. -- Add new features for ptn3460, step-by-step. -- Address comments from Thierry and Andreas for ptn3460 and ps8622. -- Split documentation patches from driver patches.
"[PATCH V7 5/12] drm/exynos: dp: support drm_bridge" introduces following Kconfig error: drivers/video/fbdev/Kconfig:5:error: recursive dependency detected! drivers/video/fbdev/Kconfig:5: symbol FB is selected by DRM_KMS_FB_HELPER drivers/gpu/drm/Kconfig:39: symbol DRM_KMS_FB_HELPER depends on DRM_KMS_HELPER drivers/gpu/drm/Kconfig:33: symbol DRM_KMS_HELPER is selected by DRM_BRIDGE drivers/gpu/drm/bridge/Kconfig:1: symbol DRM_BRIDGE is selected by DRM_EXYNOS_DP drivers/gpu/drm/exynos/Kconfig:53: symbol DRM_EXYNOS_DP depends on DRM_EXYNOS_FIMD drivers/gpu/drm/exynos/Kconfig:28: symbol DRM_EXYNOS_FIMD depends on FB_S3C drivers/video/fbdev/Kconfig:2038: symbol FB_S3C depends on FB
What's the best way to fix the above ambiguity?
Ajay Kumar (11): [PATCH V7 1/12] drm/bridge: ptn3460: Few trivial cleanups [PATCH V7 2/12] drm/bridge: do not pass drm_bridge_funcs to drm_bridge_init [PATCH V7 3/12] drm/bridge: Add helper functions for drm_bridge [PATCH V7 4/12] drm/bridge: ptn3460: convert to i2c driver model [PATCH V7 5/12] drm/exynos: dp: support drm_bridge [PATCH V7 6/12] drm/bridge: ptn3460: support drm_panel [PATCH V7 7/12] drm/bridge: ptn3460: probe connector at the end of bridge attach [PATCH V7 8/12] drm/bridge: ptn3460: use gpiod interface [PATCH V7 9/12] Documentation: drm: bridge: move to video/bridge [PATCH V7 10/12] Documentation: devicetree: Add vendor prefix for parade [PATCH V7 11/12] Documentation: bridge: Add documentation for ps8622 DT properties
Vincent Palatin (1): [PATCH V7 12/12] drm/bridge: Add i2c based driver for ps8622/ps8625 bridge
.../devicetree/bindings/drm/bridge/ptn3460.txt | 27 - .../devicetree/bindings/vendor-prefixes.txt | 1 + .../devicetree/bindings/video/bridge/ps8622.txt | 20 + .../devicetree/bindings/video/bridge/ptn3460.txt | 27 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 30 +- drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ps8622.c | 681 ++++++++++++++++++++ drivers/gpu/drm/bridge/ptn3460.c | 302 +++++---- drivers/gpu/drm/drm_bridge.c | 102 +++ drivers/gpu/drm/drm_crtc.c | 9 +- drivers/gpu/drm/exynos/Kconfig | 3 +- drivers/gpu/drm/exynos/exynos_dp_core.c | 54 +- drivers/gpu/drm/exynos/exynos_dp_core.h | 1 + drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 7 +- drivers/gpu/drm/sti/sti_hda.c | 3 +- drivers/gpu/drm/sti/sti_hdmi.c | 3 +- include/drm/drm_crtc.h | 15 +- 18 files changed, 1098 insertions(+), 189 deletions(-) delete mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt create mode 100644 Documentation/devicetree/bindings/video/bridge/ps8622.txt create mode 100644 Documentation/devicetree/bindings/video/bridge/ptn3460.txt create mode 100644 drivers/gpu/drm/bridge/ps8622.c create mode 100644 drivers/gpu/drm/drm_bridge.c
This patch does the following changes: -- Use usleep_range instead of udelay. -- Remove driver_private member from ptn3460 structure. -- Make all possible functions and structures static. -- Use dev_err for non-DRM errors. -- Arrange header files alphabetically. -- s/edid/EDID in all error messages.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/bridge/ptn3460.c | 95 +++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 47 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index d466696..4db38e1 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -13,19 +13,19 @@ * GNU General Public License for more details. */
+#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_gpio.h> -#include <linux/i2c.h> -#include <linux/gpio.h> -#include <linux/delay.h>
-#include "drmP.h" -#include "drm_edid.h" +#include "bridge/ptn3460.h" + #include "drm_crtc.h" #include "drm_crtc_helper.h" - -#include "bridge/ptn3460.h" +#include "drm_edid.h" +#include "drmP.h"
#define PTN3460_EDID_ADDR 0x0 #define PTN3460_EDID_EMULATION_ADDR 0x84 @@ -37,7 +37,7 @@ struct ptn3460_bridge { struct drm_connector connector; struct i2c_client *client; struct drm_encoder *encoder; - struct drm_bridge *bridge; + struct drm_bridge bridge; struct edid *edid; int gpio_pd_n; int gpio_rst_n; @@ -45,6 +45,18 @@ struct ptn3460_bridge { bool enabled; };
+static inline struct ptn3460_bridge * + bridge_to_ptn3460(struct drm_bridge *bridge) +{ + return container_of(bridge, struct ptn3460_bridge, bridge); +} + +static inline struct ptn3460_bridge * + connector_to_ptn3460(struct drm_connector *connector) +{ + return container_of(connector, struct ptn3460_bridge, connector); +} + static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr, u8 *buf, int len) { @@ -92,7 +104,7 @@ static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge) ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR, ptn_bridge->edid_emulation); if (ret) { - DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret); + DRM_ERROR("Failed to transfer EDID to sram, ret=%d\n", ret); return ret; }
@@ -102,7 +114,7 @@ static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val); if (ret) { - DRM_ERROR("Failed to write edid value, ret=%d\n", ret); + DRM_ERROR("Failed to write EDID value, ret=%d\n", ret); return ret; }
@@ -111,7 +123,7 @@ static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge)
static void ptn3460_pre_enable(struct drm_bridge *bridge) { - struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); int ret;
if (ptn_bridge->enabled) @@ -122,7 +134,7 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
if (gpio_is_valid(ptn_bridge->gpio_rst_n)) { gpio_set_value(ptn_bridge->gpio_rst_n, 0); - udelay(10); + usleep_range(10, 20); gpio_set_value(ptn_bridge->gpio_rst_n, 1); }
@@ -135,7 +147,7 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
ret = ptn3460_select_edid(ptn_bridge); if (ret) - DRM_ERROR("Select edid failed ret=%d\n", ret); + DRM_ERROR("Select EDID failed ret=%d\n", ret);
ptn_bridge->enabled = true; } @@ -146,7 +158,7 @@ static void ptn3460_enable(struct drm_bridge *bridge)
static void ptn3460_disable(struct drm_bridge *bridge) { - struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
if (!ptn_bridge->enabled) return; @@ -164,9 +176,9 @@ static void ptn3460_post_disable(struct drm_bridge *bridge) { }
-void ptn3460_bridge_destroy(struct drm_bridge *bridge) +static void ptn3460_bridge_destroy(struct drm_bridge *bridge) { - struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge);
drm_bridge_cleanup(bridge); if (gpio_is_valid(ptn_bridge->gpio_pd_n)) @@ -176,7 +188,7 @@ void ptn3460_bridge_destroy(struct drm_bridge *bridge) /* Nothing else to free, we've got devm allocated memory */ }
-struct drm_bridge_funcs ptn3460_bridge_funcs = { +static struct drm_bridge_funcs ptn3460_bridge_funcs = { .pre_enable = ptn3460_pre_enable, .enable = ptn3460_enable, .disable = ptn3460_disable, @@ -184,24 +196,24 @@ struct drm_bridge_funcs ptn3460_bridge_funcs = { .destroy = ptn3460_bridge_destroy, };
-int ptn3460_get_modes(struct drm_connector *connector) +static int ptn3460_get_modes(struct drm_connector *connector) { struct ptn3460_bridge *ptn_bridge; u8 *edid; - int ret, num_modes; + int ret, num_modes = 0; bool power_off;
- ptn_bridge = container_of(connector, struct ptn3460_bridge, connector); + ptn_bridge = connector_to_ptn3460(connector);
if (ptn_bridge->edid) return drm_add_edid_modes(connector, ptn_bridge->edid);
power_off = !ptn_bridge->enabled; - ptn3460_pre_enable(ptn_bridge->bridge); + ptn3460_pre_enable(&ptn_bridge->bridge);
edid = kmalloc(EDID_LENGTH, GFP_KERNEL); if (!edid) { - DRM_ERROR("Failed to allocate edid\n"); + DRM_ERROR("Failed to allocate EDID\n"); return 0; }
@@ -209,7 +221,6 @@ int ptn3460_get_modes(struct drm_connector *connector) EDID_LENGTH); if (ret) { kfree(edid); - num_modes = 0; goto out; }
@@ -220,37 +231,35 @@ int ptn3460_get_modes(struct drm_connector *connector)
out: if (power_off) - ptn3460_disable(ptn_bridge->bridge); + ptn3460_disable(&ptn_bridge->bridge);
return num_modes; }
-struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) +static struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) { - struct ptn3460_bridge *ptn_bridge; - - ptn_bridge = container_of(connector, struct ptn3460_bridge, connector); + struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector);
return ptn_bridge->encoder; }
-struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { +static struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { .get_modes = ptn3460_get_modes, .best_encoder = ptn3460_best_encoder, };
-enum drm_connector_status ptn3460_detect(struct drm_connector *connector, +static enum drm_connector_status ptn3460_detect(struct drm_connector *connector, bool force) { return connector_status_connected; }
-void ptn3460_connector_destroy(struct drm_connector *connector) +static void ptn3460_connector_destroy(struct drm_connector *connector) { drm_connector_cleanup(connector); }
-struct drm_connector_funcs ptn3460_connector_funcs = { +static struct drm_connector_funcs ptn3460_connector_funcs = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .detect = ptn3460_detect, @@ -261,30 +270,22 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, struct i2c_client *client, struct device_node *node) { int ret; - struct drm_bridge *bridge; struct ptn3460_bridge *ptn_bridge;
- bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL); - if (!bridge) { - DRM_ERROR("Failed to allocate drm bridge\n"); - return -ENOMEM; - } - ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL); if (!ptn_bridge) { - DRM_ERROR("Failed to allocate ptn bridge\n"); return -ENOMEM; }
ptn_bridge->client = client; ptn_bridge->encoder = encoder; - ptn_bridge->bridge = bridge; ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0); if (gpio_is_valid(ptn_bridge->gpio_pd_n)) { ret = gpio_request_one(ptn_bridge->gpio_pd_n, GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N"); if (ret) { - DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret); + dev_err(&client->dev, + "Request powerdown-gpio failed (%d)\n", ret); return ret; } } @@ -298,7 +299,8 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, ret = gpio_request_one(ptn_bridge->gpio_rst_n, GPIOF_OUT_INIT_LOW, "PTN3460_RST_N"); if (ret) { - DRM_ERROR("Request reset-gpio failed (%d)\n", ret); + dev_err(&client->dev, + "Request reset-gpio failed (%d)\n", ret); gpio_free(ptn_bridge->gpio_pd_n); return ret; } @@ -307,18 +309,17 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, ret = of_property_read_u32(node, "edid-emulation", &ptn_bridge->edid_emulation); if (ret) { - DRM_ERROR("Can't read edid emulation value\n"); + dev_err(&client->dev, "Can't read EDID emulation value\n"); goto err; }
- ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs); + ret = drm_bridge_init(dev, &ptn_bridge->bridge, &ptn3460_bridge_funcs); if (ret) { DRM_ERROR("Failed to initialize bridge with drm\n"); goto err; }
- bridge->driver_private = ptn_bridge; - encoder->bridge = bridge; + encoder->bridge = &ptn_bridge->bridge;
ret = drm_connector_init(dev, &ptn_bridge->connector, &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
Assign the pointer to bridge ops structure(drm_bridge_funcs) in the bridge driver itself, instead of passing it to drm_bridge_init.
This will allow bridge driver developer to pack bridge private information inside the bridge object and pass only the drm-relevant information to drm_bridge_init.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/bridge/ptn3460.c | 3 ++- drivers/gpu/drm/drm_crtc.c | 5 +---- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 3 ++- drivers/gpu/drm/sti/sti_hda.c | 3 ++- drivers/gpu/drm/sti/sti_hdmi.c | 3 ++- include/drm/drm_crtc.h | 3 +-- 6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index 4db38e1..a2ddc8d 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -313,7 +313,8 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, goto err; }
- ret = drm_bridge_init(dev, &ptn_bridge->bridge, &ptn3460_bridge_funcs); + ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs; + ret = drm_bridge_init(dev, &ptn_bridge->bridge); if (ret) { DRM_ERROR("Failed to initialize bridge with drm\n"); goto err; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fa2be24..25f5cfa 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1022,7 +1022,6 @@ EXPORT_SYMBOL(drm_connector_unplug_all); * drm_bridge_init - initialize a drm transcoder/bridge * @dev: drm device * @bridge: transcoder/bridge to set up - * @funcs: bridge function table * * Initialises a preallocated bridge. Bridges should be * subclassed as part of driver connector objects. @@ -1030,8 +1029,7 @@ EXPORT_SYMBOL(drm_connector_unplug_all); * Returns: * Zero on success, error code on failure. */ -int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, - const struct drm_bridge_funcs *funcs) +int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge) { int ret;
@@ -1042,7 +1040,6 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, goto out;
bridge->dev = dev; - bridge->funcs = funcs;
list_add_tail(&bridge->head, &dev->mode_config.bridge_list); dev->mode_config.num_bridge++; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index f6cf745..0309539 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -221,8 +221,9 @@ struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi) hdmi_bridge->hdmi = hdmi_reference(hdmi);
bridge = &hdmi_bridge->base; + bridge->funcs = &hdmi_bridge_funcs;
- drm_bridge_init(hdmi->dev, bridge, &hdmi_bridge_funcs); + drm_bridge_init(hdmi->dev, bridge);
return bridge;
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 72d957f..b3344c7 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -664,7 +664,8 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) return -ENOMEM;
bridge->driver_private = hda; - drm_bridge_init(drm_dev, bridge, &sti_hda_bridge_funcs); + bridge->funcs = &sti_hda_bridge_funcs; + drm_bridge_init(drm_dev, bridge);
encoder->bridge = bridge; connector->encoder = encoder; diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 284e541..3ad319f 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -629,7 +629,8 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) return -ENOMEM;
bridge->driver_private = hdmi; - drm_bridge_init(drm_dev, bridge, &sti_hdmi_bridge_funcs); + bridge->funcs = &sti_hdmi_bridge_funcs; + drm_bridge_init(drm_dev, bridge);
encoder->bridge = bridge; connector->encoder = encoder; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f1105d0..f48a436 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -906,8 +906,7 @@ extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ extern void drm_connector_unplug_all(struct drm_device *dev);
-extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, - const struct drm_bridge_funcs *funcs); +extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge); extern void drm_bridge_cleanup(struct drm_bridge *bridge);
extern int drm_encoder_init(struct drm_device *dev,
On Wed, Aug 27, 2014 at 10:29 AM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
Assign the pointer to bridge ops structure(drm_bridge_funcs) in the bridge driver itself, instead of passing it to drm_bridge_init.
This will allow bridge driver developer to pack bridge private information inside the bridge object and pass only the drm-relevant information to drm_bridge_init.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
This matches how drm_panel works, which it looks like this set is trying to emulate.
Acked-by: Sean Paul seanpaul@chromium.org
drivers/gpu/drm/bridge/ptn3460.c | 3 ++- drivers/gpu/drm/drm_crtc.c | 5 +---- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 3 ++- drivers/gpu/drm/sti/sti_hda.c | 3 ++- drivers/gpu/drm/sti/sti_hdmi.c | 3 ++- include/drm/drm_crtc.h | 3 +-- 6 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index 4db38e1..a2ddc8d 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -313,7 +313,8 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, goto err; }
ret = drm_bridge_init(dev, &ptn_bridge->bridge, &ptn3460_bridge_funcs);
ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs;
ret = drm_bridge_init(dev, &ptn_bridge->bridge); if (ret) { DRM_ERROR("Failed to initialize bridge with drm\n"); goto err;
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index fa2be24..25f5cfa 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1022,7 +1022,6 @@ EXPORT_SYMBOL(drm_connector_unplug_all);
- drm_bridge_init - initialize a drm transcoder/bridge
- @dev: drm device
- @bridge: transcoder/bridge to set up
- @funcs: bridge function table
- Initialises a preallocated bridge. Bridges should be
- subclassed as part of driver connector objects.
@@ -1030,8 +1029,7 @@ EXPORT_SYMBOL(drm_connector_unplug_all);
- Returns:
- Zero on success, error code on failure.
*/ -int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
const struct drm_bridge_funcs *funcs)
+int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge) { int ret;
@@ -1042,7 +1040,6 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, goto out;
bridge->dev = dev;
bridge->funcs = funcs; list_add_tail(&bridge->head, &dev->mode_config.bridge_list); dev->mode_config.num_bridge++;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index f6cf745..0309539 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -221,8 +221,9 @@ struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi) hdmi_bridge->hdmi = hdmi_reference(hdmi);
bridge = &hdmi_bridge->base;
bridge->funcs = &hdmi_bridge_funcs;
drm_bridge_init(hdmi->dev, bridge, &hdmi_bridge_funcs);
drm_bridge_init(hdmi->dev, bridge); return bridge;
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 72d957f..b3344c7 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -664,7 +664,8 @@ static int sti_hda_bind(struct device *dev, struct device *master, void *data) return -ENOMEM;
bridge->driver_private = hda;
drm_bridge_init(drm_dev, bridge, &sti_hda_bridge_funcs);
bridge->funcs = &sti_hda_bridge_funcs;
drm_bridge_init(drm_dev, bridge); encoder->bridge = bridge; connector->encoder = encoder;
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c index 284e541..3ad319f 100644 --- a/drivers/gpu/drm/sti/sti_hdmi.c +++ b/drivers/gpu/drm/sti/sti_hdmi.c @@ -629,7 +629,8 @@ static int sti_hdmi_bind(struct device *dev, struct device *master, void *data) return -ENOMEM;
bridge->driver_private = hdmi;
drm_bridge_init(drm_dev, bridge, &sti_hdmi_bridge_funcs);
bridge->funcs = &sti_hdmi_bridge_funcs;
drm_bridge_init(drm_dev, bridge); encoder->bridge = bridge; connector->encoder = encoder;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f1105d0..f48a436 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -906,8 +906,7 @@ extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ extern void drm_connector_unplug_all(struct drm_device *dev);
-extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge,
const struct drm_bridge_funcs *funcs);
+extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge); extern void drm_bridge_cleanup(struct drm_bridge *bridge);
extern int drm_encoder_init(struct drm_device *dev,
1.7.9.5
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 15 ++++- drivers/gpu/drm/drm_bridge.c | 102 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc.c | 4 +- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 4 +- include/drm/drm_crtc.h | 12 +++- 6 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/drm_bridge.c
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4a55d59..bdbfb6f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -19,6 +19,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o +drm-$(CONFIG_DRM_BRIDGE) += drm_bridge.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 884923f..5a8e907 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -1,5 +1,16 @@ -config DRM_PTN3460 - tristate "PTN3460 DP/LVDS bridge" +config DRM_BRIDGE + tristate depends on DRM select DRM_KMS_HELPER + help + Bridge registration and lookup framework. + +menu "bridge chips" + depends on DRM_BRIDGE + +config DRM_PTN3460 + tristate "PTN3460 DP/LVDS bridge" + depends on DRM_BRIDGE ---help--- + +endmenu diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c new file mode 100644 index 0000000..b2d43fd --- /dev/null +++ b/drivers/gpu/drm/drm_bridge.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <linux/err.h> +#include <linux/module.h> + +#include <drm/drm_crtc.h> + +#include "drm/drmP.h" + +static DEFINE_MUTEX(bridge_lock); +static LIST_HEAD(bridge_list); + +int drm_bridge_add(struct drm_bridge *bridge) +{ + mutex_lock(&bridge_lock); + list_add_tail(&bridge->list, &bridge_list); + mutex_unlock(&bridge_lock); + + return 0; +} +EXPORT_SYMBOL(drm_bridge_add); + +void drm_bridge_remove(struct drm_bridge *bridge) +{ + mutex_lock(&bridge_lock); + list_del_init(&bridge->list); + mutex_unlock(&bridge_lock); +} +EXPORT_SYMBOL(drm_bridge_remove); + +int drm_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder) +{ + int ret; + + if (!bridge || !encoder) + return -EINVAL; + + if (bridge->encoder) + return -EBUSY; + + encoder->bridge = bridge; + bridge->encoder = encoder; + bridge->drm = encoder->dev; + + ret = drm_bridge_init(bridge->drm, bridge); + if (ret) { + DRM_ERROR("Failed to register bridge with drm\n"); + return ret; + } + + if (bridge->funcs->attach) + return bridge->funcs->attach(bridge); + + return 0; +} +EXPORT_SYMBOL(drm_bridge_attach); + +#ifdef CONFIG_OF +struct drm_bridge *of_drm_find_bridge(struct device_node *np) +{ + struct drm_bridge *bridge; + + mutex_lock(&bridge_lock); + + list_for_each_entry(bridge, &bridge_list, list) { + if (bridge->dev->of_node == np) { + mutex_unlock(&bridge_lock); + return bridge; + } + } + + mutex_unlock(&bridge_lock); + return NULL; +} +EXPORT_SYMBOL(of_drm_find_bridge); +#endif + +MODULE_AUTHOR("Ajay Kumar ajaykumar.rs@samsung.com"); +MODULE_DESCRIPTION("DRM bridge infrastructure"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 25f5cfa..2fb22fa 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1039,7 +1039,7 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge) if (ret) goto out;
- bridge->dev = dev; + bridge->drm = dev;
list_add_tail(&bridge->head, &dev->mode_config.bridge_list); dev->mode_config.num_bridge++; @@ -1058,7 +1058,7 @@ EXPORT_SYMBOL(drm_bridge_init); */ void drm_bridge_cleanup(struct drm_bridge *bridge) { - struct drm_device *dev = bridge->dev; + struct drm_device *dev = bridge->drm;
drm_modeset_lock_all(dev); drm_mode_object_put(dev, &bridge->base); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 0309539..bc9e5ff 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -33,7 +33,7 @@ static void hdmi_bridge_destroy(struct drm_bridge *bridge)
static void power_on(struct drm_bridge *bridge) { - struct drm_device *dev = bridge->dev; + struct drm_device *dev = bridge->drm; struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; const struct hdmi_platform_config *config = hdmi->config; @@ -67,7 +67,7 @@ static void power_on(struct drm_bridge *bridge)
static void power_off(struct drm_bridge *bridge) { - struct drm_device *dev = bridge->dev; + struct drm_device *dev = bridge->drm; struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; const struct hdmi_platform_config *config = hdmi->config; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f48a436..f6f426f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -629,6 +629,7 @@ struct drm_plane {
/** * drm_bridge_funcs - drm_bridge control functions + * @attach: Called during drm_bridge_attach * @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge * @disable: Called right before encoder prepare, disables the bridge * @post_disable: Called right after encoder prepare, for lockstepped disable @@ -638,6 +639,7 @@ struct drm_plane { * @destroy: make object go away */ struct drm_bridge_funcs { + int (*attach)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); @@ -660,8 +662,11 @@ struct drm_bridge_funcs { * @driver_private: pointer to the bridge driver's internal context */ struct drm_bridge { - struct drm_device *dev; + struct device *dev; + struct drm_device *drm; + struct drm_encoder *encoder; struct list_head head; + struct list_head list;
struct drm_mode_object base;
@@ -906,6 +911,11 @@ extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ extern void drm_connector_unplug_all(struct drm_device *dev);
+extern int drm_bridge_add(struct drm_bridge *bridge); +extern void drm_bridge_remove(struct drm_bridge *bridge); +extern struct drm_bridge *of_drm_find_bridge(struct device_node *np); +extern int drm_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder); extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge); extern void drm_bridge_cleanup(struct drm_bridge *bridge);
So don't ask why but I accidentally ended up in a branch looking at this patch and didn't like it. So very quick&grumpy review.
First, please make the patch subject more descriptive: I'd expect a helper function scaffolding like the various crtc/probe/dp ... helpers we already have. You instead add code to untangle the probe ordering. Please say so.
More comments below.
On Wed, Aug 27, 2014 at 07:59:37PM +0530, Ajay Kumar wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
[snip]
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
- struct drm_device *dev;
- struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
- struct drm_device *drm;
- struct drm_encoder *encoder;
This breaks bridge->bridge chaining (if we ever get there). It seems pretty much unused anyway except for an EBUSY check. Can't you use bridge->dev for that?
struct list_head head;
- struct list_head list;
These lists need better names. I know that the "head" is really awful, especially since it's actually not the head of the list but just an element.
struct drm_mode_object base;
Aside: I've noticed all this trying to update the kerneldoc for struct drm_bridge, which just showed that this patch makes inconsistent changes. Trying to write kerneldoc is a really great way to come up with better interfaces imo.
Cheers, Daniel
@@ -906,6 +911,11 @@ extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ extern void drm_connector_unplug_all(struct drm_device *dev);
+extern int drm_bridge_add(struct drm_bridge *bridge); +extern void drm_bridge_remove(struct drm_bridge *bridge); +extern struct drm_bridge *of_drm_find_bridge(struct device_node *np); +extern int drm_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder);
extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge); extern void drm_bridge_cleanup(struct drm_bridge *bridge);
-- 1.7.9.5
On Mon, Oct 27, 2014 at 3:01 PM, Daniel Vetter daniel@ffwll.ch wrote:
So don't ask why but I accidentally ended up in a branch looking at this patch and didn't like it. So very quick&grumpy review.
First, please make the patch subject more descriptive: I'd expect a helper function scaffolding like the various crtc/probe/dp ... helpers we already have. You instead add code to untangle the probe ordering. Please say so.
More comments below.
On Wed, Aug 27, 2014 at 07:59:37PM +0530, Ajay Kumar wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
[snip]
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
struct drm_device *drm;
struct drm_encoder *encoder;
This breaks bridge->bridge chaining (if we ever get there). It seems pretty much unused anyway except for an EBUSY check. Can't you use bridge->dev for that?
Yeah, I'd prefer to pass drm_device directly into drm_bridge_attach and leave it up to the caller to establish the proper chain.
struct list_head head;
struct list_head list;
These lists need better names. I know that the "head" is really awful, especially since it's actually not the head of the list but just an element.
I think we can just rip bridge_list out of mode_config if we're going to keep track of bridges elsewhere. So we can nuke "head" and keep "list". This also means that bridge->destroy() goes away, but that's probably Ok if everything converts to the standalone driver model where we have driver->remove()
Sean
struct drm_mode_object base;
Aside: I've noticed all this trying to update the kerneldoc for struct drm_bridge, which just showed that this patch makes inconsistent changes. Trying to write kerneldoc is a really great way to come up with better interfaces imo.
Cheers, Daniel
@@ -906,6 +911,11 @@ extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ extern void drm_connector_unplug_all(struct drm_device *dev);
+extern int drm_bridge_add(struct drm_bridge *bridge); +extern void drm_bridge_remove(struct drm_bridge *bridge); +extern struct drm_bridge *of_drm_find_bridge(struct device_node *np); +extern int drm_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder);
extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge); extern void drm_bridge_cleanup(struct drm_bridge *bridge);
-- 1.7.9.5
-- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry? -Daniel
On Mon, Oct 27, 2014 at 11:20 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
This bridge code here though suffers from the same. So to me this looks rather fishy.
It doesn't help that we have drm_of.[hc] around but not all the of code is in there. Adding Russell too. -Daniel
On Mon, Oct 27, 2014 at 11:26:30PM +0100, Daniel Vetter wrote:
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
This bridge code here though suffers from the same. So to me this looks rather fishy.
It doesn't help that we have drm_of.[hc] around but not all the of code is in there. Adding Russell too.
I rather dropped the ball with imx-drm when things became difficult with asking Greg to pull my git tree - and as a result, I decided that I would no longer be helping with patch submission for imx-drm, nor trying to get it out of the staging tree anymore.
I do still have the patch (dated from July) in my git tree which adds it to imx-drm - see below. Rebased to 3.18-rc2 today.
I also have a patch (from April) which creates a generic OF DDC connector helper, but that remains unfinished, in the sense that it lives beside imx-drm, pulling the DDC specific code out of imx-hdmi and imx-tve, even though there's nothing imx-drm specific about it.
8<==== From: Russell King rmk+kernel@arm.linux.org.uk Subject: [PATCH] imx-drm: convert imx-drm to use the generic DRM OF helper
Use the generic DRM OF helper to locate the possible CRTCs for the encoder, thereby shrinking the imx-drm driver some more.
Acked-by: Philipp Zabel p.zabel@pengutronix.de Signed-off-by: Russell King rmk+kernel@arm.linux.org.uk --- drivers/staging/imx-drm/imx-drm-core.c | 72 ++++++---------------------------- 1 file changed, 13 insertions(+), 59 deletions(-)
diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c index 9cb222e2996f..5e2c1f4b9165 100644 --- a/drivers/staging/imx-drm/imx-drm-core.c +++ b/drivers/staging/imx-drm/imx-drm-core.c @@ -24,6 +24,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_of.h>
#include "imx-drm.h"
@@ -47,7 +48,6 @@ struct imx_drm_crtc { struct drm_crtc *crtc; int pipe; struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs; - struct device_node *port; };
static int legacyfb_depth = 16; @@ -366,9 +366,10 @@ int imx_drm_add_crtc(struct drm_device *drm, struct drm_crtc *crtc,
imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs; imx_drm_crtc->pipe = imxdrm->pipes++; - imx_drm_crtc->port = port; imx_drm_crtc->crtc = crtc;
+ crtc->port = port; + imxdrm->crtc[imx_drm_crtc->pipe] = imx_drm_crtc;
*new_crtc = imx_drm_crtc; @@ -409,34 +410,6 @@ int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc) } EXPORT_SYMBOL_GPL(imx_drm_remove_crtc);
-/* - * Find the DRM CRTC possible mask for the connected endpoint. - * - * The encoder possible masks are defined by their position in the - * mode_config crtc_list. This means that CRTCs must not be added - * or removed once the DRM device has been fully initialised. - */ -static uint32_t imx_drm_find_crtc_mask(struct imx_drm_device *imxdrm, - struct device_node *endpoint) -{ - struct device_node *port; - unsigned i; - - port = of_graph_get_remote_port(endpoint); - if (!port) - return 0; - of_node_put(port); - - for (i = 0; i < MAX_CRTC; i++) { - struct imx_drm_crtc *imx_drm_crtc = imxdrm->crtc[i]; - - if (imx_drm_crtc && imx_drm_crtc->port == port) - return drm_crtc_mask(imx_drm_crtc->crtc); - } - - return 0; -} - static struct device_node *imx_drm_of_get_next_endpoint( const struct device_node *parent, struct device_node *prev) { @@ -449,35 +422,16 @@ static struct device_node *imx_drm_of_get_next_endpoint( int imx_drm_encoder_parse_of(struct drm_device *drm, struct drm_encoder *encoder, struct device_node *np) { - struct imx_drm_device *imxdrm = drm->dev_private; - struct device_node *ep = NULL; - uint32_t crtc_mask = 0; - int i; + uint32_t crtc_mask = drm_of_find_possible_crtcs(drm, np);
- for (i = 0; ; i++) { - u32 mask; - - ep = imx_drm_of_get_next_endpoint(np, ep); - if (!ep) - break; - - mask = imx_drm_find_crtc_mask(imxdrm, ep); - - /* - * If we failed to find the CRTC(s) which this encoder is - * supposed to be connected to, it's because the CRTC has - * not been registered yet. Defer probing, and hope that - * the required CRTC is added later. - */ - if (mask == 0) - return -EPROBE_DEFER; - - crtc_mask |= mask; - } - - of_node_put(ep); - if (i == 0) - return -ENOENT; + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (crtc_mask == 0) + return -EPROBE_DEFER;
encoder->possible_crtcs = crtc_mask;
@@ -511,7 +465,7 @@ int imx_drm_encoder_get_mux_id(struct device_node *node,
port = of_graph_get_remote_port(ep); of_node_put(port); - if (port == imx_crtc->port) { + if (port == imx_crtc->crtc->port) { ret = of_graph_parse_endpoint(ep, &endpoint); return ret ? ret : endpoint.port; }
Hi Russell,
Am Montag, den 27.10.2014, 23:57 +0000 schrieb Russell King - ARM Linux:
On Mon, Oct 27, 2014 at 11:26:30PM +0100, Daniel Vetter wrote:
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
This bridge code here though suffers from the same. So to me this looks rather fishy.
It doesn't help that we have drm_of.[hc] around but not all the of code is in there. Adding Russell too.
I rather dropped the ball with imx-drm when things became difficult with asking Greg to pull my git tree - and as a result, I decided that I would no longer be helping with patch submission for imx-drm, nor trying to get it out of the staging tree anymore.
In that case I'd like to take up the ball.
In my opinion the driver is now in a state in which it could be moved to gpu/drm first and then fixed up there (all the blockers in drivers/staging/imx-drm/TODO are gone). That would also give us a single path for all imx-drm/ipu-v3 related patches again. My main motivation to put it into staging in the first place was that the device tree bindings had to be worked out, but now it only results in a coordination nightmare.
regards Philipp
On Mon, Oct 27, 2014 at 11:26:30PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 11:20 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
I don't understand. Panels are global resources that get registered by some driver that isn't tied to the DRM device until attached. It can't be in a per-DRM device list, because it's external to the device.
And yes, they can go away when a driver is unloaded, though it's not the typical use-case.
This bridge code here though suffers from the same. So to me this looks rather fishy.
Well, this version of the DRM bridge support was written to be close to DRM panel, so it isn't really surprising that it's similar =), but like I said, I don't really understand what you think is wrong with it.
It doesn't help that we have drm_of.[hc] around but not all the of code is in there. Adding Russell too.
DRM panel and DRM bridge aren't just OF helpers. They can be used with whatever type of device you want.
Thierry
On Tue, Oct 28, 2014 at 03:35:50PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:26:30PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 11:20 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
I don't understand. Panels are global resources that get registered by some driver that isn't tied to the DRM device until attached. It can't be in a per-DRM device list, because it's external to the device.
And yes, they can go away when a driver is unloaded, though it's not the typical use-case.
This bridge code here though suffers from the same. So to me this looks rather fishy.
Well, this version of the DRM bridge support was written to be close to DRM panel, so it isn't really surprising that it's similar =), but like I said, I don't really understand what you think is wrong with it.
You have a mutex to protect the global list of bridges/panels. But if you do a lookup you don't grab a reference count on the panel, so the moment you drop the list mutex the panel/bridge can go away.
Yes usually you don't unload a driver on a soc but soc isn't everything and designing new stuff to not be hotunplug save isn't great either. -Daniel
On Wed, Oct 29, 2014 at 08:43:14AM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 03:35:50PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:26:30PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 11:20 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
> @@ -660,8 +662,11 @@ struct drm_bridge_funcs { > * @driver_private: pointer to the bridge driver's internal context > */ > struct drm_bridge { > - struct drm_device *dev; > + struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
I don't understand. Panels are global resources that get registered by some driver that isn't tied to the DRM device until attached. It can't be in a per-DRM device list, because it's external to the device.
And yes, they can go away when a driver is unloaded, though it's not the typical use-case.
This bridge code here though suffers from the same. So to me this looks rather fishy.
Well, this version of the DRM bridge support was written to be close to DRM panel, so it isn't really surprising that it's similar =), but like I said, I don't really understand what you think is wrong with it.
You have a mutex to protect the global list of bridges/panels. But if you do a lookup you don't grab a reference count on the panel, so the moment you drop the list mutex the panel/bridge can go away.
Yes usually you don't unload a driver on a soc but soc isn't everything and designing new stuff to not be hotunplug save isn't great either.
Yeah, I certainly agree that adding proper reference counting would be a good thing. I think perhaps we could just take a reference on the struct device * to prevent it from disappearing.
Although perhaps I misunderstand what you mean by "go away".
Thierry
On Wed, Oct 29, 2014 at 09:38:23AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 08:43:14AM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 03:35:50PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:26:30PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 11:20 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
>> @@ -660,8 +662,11 @@ struct drm_bridge_funcs { >> * @driver_private: pointer to the bridge driver's internal context >> */ >> struct drm_bridge { >> - struct drm_device *dev; >> + struct device *dev; > > Please don't rename the ->dev pointer into drm. Because _all_ the other > drm structures still call it ->dev. Also, can't we use struct device_node > here like we do in the of helpers Russell added? See 7e435aad38083 >
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
I don't understand. Panels are global resources that get registered by some driver that isn't tied to the DRM device until attached. It can't be in a per-DRM device list, because it's external to the device.
And yes, they can go away when a driver is unloaded, though it's not the typical use-case.
This bridge code here though suffers from the same. So to me this looks rather fishy.
Well, this version of the DRM bridge support was written to be close to DRM panel, so it isn't really surprising that it's similar =), but like I said, I don't really understand what you think is wrong with it.
You have a mutex to protect the global list of bridges/panels. But if you do a lookup you don't grab a reference count on the panel, so the moment you drop the list mutex the panel/bridge can go away.
Yes usually you don't unload a driver on a soc but soc isn't everything and designing new stuff to not be hotunplug save isn't great either.
Yeah, I certainly agree that adding proper reference counting would be a good thing. I think perhaps we could just take a reference on the struct device * to prevent it from disappearing.
Although perhaps I misunderstand what you mean by "go away".
I meant the drm_panel/bridge could go away any second, since nothing prevents a module unload of the panel/bridge driver. So in theory you could get the unregister call right after you've done the lookup. Which means the bridge/panel pointer you've just returned is freed memory.
I think we nee try_get_module for the code and kref on the actual data structures. That makes the entire thing a bit non-trivial, which is why I think it would be better as some generic helper. Which then gets embedded or instantiated for specific cases, like dt&drm_panel or dt&drm_bridge. Or maybe even acpi&drm_bridge, who knows ;-) -Daniel
On Wed, Oct 29, 2014 at 09:57:02AM +0100, Daniel Vetter wrote:
On Wed, Oct 29, 2014 at 09:38:23AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 08:43:14AM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 03:35:50PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:26:30PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 11:20 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote: >>> @@ -660,8 +662,11 @@ struct drm_bridge_funcs { >>> * @driver_private: pointer to the bridge driver's internal context >>> */ >>> struct drm_bridge { >>> - struct drm_device *dev; >>> + struct device *dev; >> >> Please don't rename the ->dev pointer into drm. Because _all_ the other >> drm structures still call it ->dev. Also, can't we use struct device_node >> here like we do in the of helpers Russell added? See 7e435aad38083 >> > > I think this is modeled after the naming in drm_panel, FWIW. However, > seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
I don't understand. Panels are global resources that get registered by some driver that isn't tied to the DRM device until attached. It can't be in a per-DRM device list, because it's external to the device.
And yes, they can go away when a driver is unloaded, though it's not the typical use-case.
This bridge code here though suffers from the same. So to me this looks rather fishy.
Well, this version of the DRM bridge support was written to be close to DRM panel, so it isn't really surprising that it's similar =), but like I said, I don't really understand what you think is wrong with it.
You have a mutex to protect the global list of bridges/panels. But if you do a lookup you don't grab a reference count on the panel, so the moment you drop the list mutex the panel/bridge can go away.
Yes usually you don't unload a driver on a soc but soc isn't everything and designing new stuff to not be hotunplug save isn't great either.
Yeah, I certainly agree that adding proper reference counting would be a good thing. I think perhaps we could just take a reference on the struct device * to prevent it from disappearing.
Although perhaps I misunderstand what you mean by "go away".
I meant the drm_panel/bridge could go away any second, since nothing prevents a module unload of the panel/bridge driver. So in theory you could get the unregister call right after you've done the lookup. Which means the bridge/panel pointer you've just returned is freed memory.
Ah yes, I see now.
I think we nee try_get_module for the code and kref on the actual data structures.
Agreed, that should do the trick. We'd probably need some sort of logic to also make operations return something like -ENODEV when the underlying device has disappeared. I think David had introduced something similar for DRM device not so long ago?
That makes the entire thing a bit non-trivial, which is why I think it would be better as some generic helper. Which then gets embedded or instantiated for specific cases, like dt&drm_panel or dt&drm_bridge. Or maybe even acpi&drm_bridge, who knows ;-)
I worry a little about type safety. How will this generic helper know what registry to look in for? Or conversely, if all these resources are added to a single registry how do you know that they're of the correct type? Failing to ensure this could cause situations where you're asking for a panel and get a bridge in return because you've wrongly wired it up in device tree for example.
But perhaps if both the registry and the device parts are turned into helpers we could still have a single core implementation and then instantiate that for each type, something roughly like this:
struct registry { struct list_head list; struct mutex lock; };
struct registry_record { struct list_head list; struct module *owner; struct kref *ref;
struct device *dev; };
int registry_add(struct registry *registry, struct registry_record *record) { ... try_module_get(record->owner); ... }
struct registry_record *registry_find_by_of_node(struct registry *registry, struct device_node *np) { ... kref_get(...); ... }
That way it should be possible to embed these into other structures, like so:
struct drm_panel { struct registry_record record; ... };
static struct registry drm_panels;
int drm_panel_add(struct drm_panel *panel) { return registry_add(&drm_panels, &panel->record); }
struct drm_panel *of_drm_panel_find(struct device_node *np) { struct registry_record *record;
record = registry_find_by_of_node(&drm_panels, np);
return container_of(record, struct drm_panel, record); }
Is that what you had in mind?
Thierry
On 10/29/2014 10:14 AM, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 09:57:02AM +0100, Daniel Vetter wrote:
On Wed, Oct 29, 2014 at 09:38:23AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 08:43:14AM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 03:35:50PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:26:30PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 11:20 PM, Daniel Vetter daniel@ffwll.ch wrote: > On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote: >>>> @@ -660,8 +662,11 @@ struct drm_bridge_funcs { >>>> * @driver_private: pointer to the bridge driver's internal context >>>> */ >>>> struct drm_bridge { >>>> - struct drm_device *dev; >>>> + struct device *dev; >>> >>> Please don't rename the ->dev pointer into drm. Because _all_ the other >>> drm structures still call it ->dev. Also, can't we use struct device_node >>> here like we do in the of helpers Russell added? See 7e435aad38083 >>> >> >> I think this is modeled after the naming in drm_panel, FWIW. However, >> seems reasonable to keep the device_node instead. > > Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with > drm_crtc drop the struct device and go directly to a struct > device_node. Since we don't really need the sturct device, the only > thing we care about is the of_node. For added bonus wrap an #ifdef > CONFIG_OF around all the various struct device_node in drm_foo.h. > Should be all fairly simple to pull off with cocci. > > Thierry?
Looking at the of_drm_find_panel function I actually wonder how that works - the drm_panel doesn't really need to stick around afaics. After all panel_list is global so some other driver can unload. Russell's of support for possible_crtcs code works differently since it only looks at per-drm_device lists.
I don't understand. Panels are global resources that get registered by some driver that isn't tied to the DRM device until attached. It can't be in a per-DRM device list, because it's external to the device.
And yes, they can go away when a driver is unloaded, though it's not the typical use-case.
This bridge code here though suffers from the same. So to me this looks rather fishy.
Well, this version of the DRM bridge support was written to be close to DRM panel, so it isn't really surprising that it's similar =), but like I said, I don't really understand what you think is wrong with it.
You have a mutex to protect the global list of bridges/panels. But if you do a lookup you don't grab a reference count on the panel, so the moment you drop the list mutex the panel/bridge can go away.
Yes usually you don't unload a driver on a soc but soc isn't everything and designing new stuff to not be hotunplug save isn't great either.
Yeah, I certainly agree that adding proper reference counting would be a good thing. I think perhaps we could just take a reference on the struct device * to prevent it from disappearing.
Although perhaps I misunderstand what you mean by "go away".
I meant the drm_panel/bridge could go away any second, since nothing prevents a module unload of the panel/bridge driver. So in theory you could get the unregister call right after you've done the lookup. Which means the bridge/panel pointer you've just returned is freed memory.
Ah yes, I see now.
I think we nee try_get_module for the code and kref on the actual data structures.
Agreed, that should do the trick. We'd probably need some sort of logic to also make operations return something like -ENODEV when the underlying device has disappeared. I think David had introduced something similar for DRM device not so long ago?
If the underlying device disappears it would be good to receive notification anyway to trigger DRM HPD event. And if we have the notification, we can release references to the device smoothly. We do not need to play tricky games with krefs, zombie data and module refcounting. On the other side component framework uses notification callbacks bind/unbind for master and components to smoothly attach/release devices, why should it be done differently in this case.
Again, look at interface_tracker [1] it does exactly what you need.
[1]: https://lkml.org/lkml/2014/4/30/345
Regards Andrzej
On Thu, Oct 30, 2014 at 11:01:02AM +0100, Andrzej Hajda wrote:
On 10/29/2014 10:14 AM, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 09:57:02AM +0100, Daniel Vetter wrote:
I think we nee try_get_module for the code and kref on the actual data structures.
Agreed, that should do the trick. We'd probably need some sort of logic to also make operations return something like -ENODEV when the underlying device has disappeared. I think David had introduced something similar for DRM device not so long ago?
If the underlying device disappears it would be good to receive notification anyway to trigger DRM HPD event. And if we have the notification, we can release references to the device smoothly. We do not need to play tricky games with krefs, zombie data and module refcounting.
Any solution which thinks it needs to lock modules into the core is fundamentally broken. It totally misses the point.
While you can lock a module into the core using try_get_module(), that doesn't stop the device itself being unbound from a driver. Soo many people have fallen into that trap. They write their device driver, along with some kind of framework which they make use try_get_module(). They think its safe. When you then echo the device name into the driver's unbind sysfs file, all hell breaks loose and the kernel oopses.
try_get_module is /totally/ useless for ensuring that things stick around.
The reality is that you can't make devices stick around. Once that remove callback from the driver layer is called, that's it, the device _is_ going away whether you like it or not. You can't stop it. It's no good returning -EBUSY, because the remove return code is ignored.
What's more scarey is when you consider that in a real hotplug situation, when the remove callback is called, the device has /already/ gone.
So please, stop thinking that try_get_module() has some magic solution. Any "solution" to device lifetimes using try_get_module() totally misses the problem, and is just mere obfuscation and actually a bug in itself.
On Thu, Oct 30, 2014 at 10:09:28AM +0000, Russell King - ARM Linux wrote:
On Thu, Oct 30, 2014 at 11:01:02AM +0100, Andrzej Hajda wrote:
On 10/29/2014 10:14 AM, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 09:57:02AM +0100, Daniel Vetter wrote:
I think we nee try_get_module for the code and kref on the actual data structures.
Agreed, that should do the trick. We'd probably need some sort of logic to also make operations return something like -ENODEV when the underlying device has disappeared. I think David had introduced something similar for DRM device not so long ago?
If the underlying device disappears it would be good to receive notification anyway to trigger DRM HPD event. And if we have the notification, we can release references to the device smoothly. We do not need to play tricky games with krefs, zombie data and module refcounting.
Any solution which thinks it needs to lock modules into the core is fundamentally broken. It totally misses the point.
While you can lock a module into the core using try_get_module(), that doesn't stop the device itself being unbound from a driver. Soo many people have fallen into that trap. They write their device driver, along with some kind of framework which they make use try_get_module(). They think its safe. When you then echo the device name into the driver's unbind sysfs file, all hell breaks loose and the kernel oopses.
try_get_module is /totally/ useless for ensuring that things stick around.
The reality is that you can't make devices stick around. Once that remove callback from the driver layer is called, that's it, the device _is_ going away whether you like it or not. You can't stop it. It's no good returning -EBUSY, because the remove return code is ignored.
What's more scarey is when you consider that in a real hotplug situation, when the remove callback is called, the device has /already/ gone.
So please, stop thinking that try_get_module() has some magic solution. Any "solution" to device lifetimes using try_get_module() totally misses the problem, and is just mere obfuscation and actually a bug in itself.
We need proper refcounting ofc, but we also need to make sure that as long as the thing is around the text section for the final unref (at least that) doesn't go poof. I'd prefer if the framework takes care of that detail and drivers could just supply a THIS_MODULE at the right place.
But fully agree on your overall point that try_get_module alone is pure snake oil. -Daniel
On Wed, Oct 29, 2014 at 10:14:37AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 09:57:02AM +0100, Daniel Vetter wrote:
That makes the entire thing a bit non-trivial, which is why I think it would be better as some generic helper. Which then gets embedded or instantiated for specific cases, like dt&drm_panel or dt&drm_bridge. Or maybe even acpi&drm_bridge, who knows ;-)
I worry a little about type safety. How will this generic helper know what registry to look in for? Or conversely, if all these resources are added to a single registry how do you know that they're of the correct type? Failing to ensure this could cause situations where you're asking for a panel and get a bridge in return because you've wrongly wired it up in device tree for example.
But perhaps if both the registry and the device parts are turned into helpers we could still have a single core implementation and then instantiate that for each type, something roughly like this:
struct registry { struct list_head list; struct mutex lock; };
struct registry_record { struct list_head list; struct module *owner; struct kref *ref;
struct device *dev;
};
int registry_add(struct registry *registry, struct registry_record *record) { ... try_module_get(record->owner); ... }
struct registry_record *registry_find_by_of_node(struct registry *registry, struct device_node *np) { ... kref_get(...); ... }
That way it should be possible to embed these into other structures, like so:
struct drm_panel { struct registry_record record; ... };
static struct registry drm_panels;
int drm_panel_add(struct drm_panel *panel) { return registry_add(&drm_panels, &panel->record); }
struct drm_panel *of_drm_panel_find(struct device_node *np) { struct registry_record *record;
record = registry_find_by_of_node(&drm_panels, np); return container_of(record, struct drm_panel, record);
}
Is that what you had in mind?
Yeah I've thought that we should instantiate using macros even, so that we have per-type registries. So you'd smash the usual set of DECLARE/DEFINE_AUX_DEV_REGISTRY into headers/source files, and they'd take a (name, key, value) tripled. For the example here(of_drm_panel, struct device_node *, struct drm_panel *) or similar. I might be hand-waving over a few too many details though ;-)
Cheers, Daniel
On Fri, Oct 31, 2014 at 04:51:43PM +0100, Daniel Vetter wrote:
On Wed, Oct 29, 2014 at 10:14:37AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 09:57:02AM +0100, Daniel Vetter wrote:
That makes the entire thing a bit non-trivial, which is why I think it would be better as some generic helper. Which then gets embedded or instantiated for specific cases, like dt&drm_panel or dt&drm_bridge. Or maybe even acpi&drm_bridge, who knows ;-)
I worry a little about type safety. How will this generic helper know what registry to look in for? Or conversely, if all these resources are added to a single registry how do you know that they're of the correct type? Failing to ensure this could cause situations where you're asking for a panel and get a bridge in return because you've wrongly wired it up in device tree for example.
But perhaps if both the registry and the device parts are turned into helpers we could still have a single core implementation and then instantiate that for each type, something roughly like this:
struct registry { struct list_head list; struct mutex lock; };
struct registry_record { struct list_head list; struct module *owner; struct kref *ref;
struct device *dev;
};
int registry_add(struct registry *registry, struct registry_record *record) { ... try_module_get(record->owner); ... }
struct registry_record *registry_find_by_of_node(struct registry *registry, struct device_node *np) { ... kref_get(...); ... }
That way it should be possible to embed these into other structures, like so:
struct drm_panel { struct registry_record record; ... };
static struct registry drm_panels;
int drm_panel_add(struct drm_panel *panel) { return registry_add(&drm_panels, &panel->record); }
struct drm_panel *of_drm_panel_find(struct device_node *np) { struct registry_record *record;
record = registry_find_by_of_node(&drm_panels, np); return container_of(record, struct drm_panel, record);
}
Is that what you had in mind?
Yeah I've thought that we should instantiate using macros even, so that we have per-type registries. So you'd smash the usual set of DECLARE/DEFINE_AUX_DEV_REGISTRY into headers/source files, and they'd take a (name, key, value) tripled. For the example here(of_drm_panel, struct device_node *, struct drm_panel *) or similar. I might be hand-waving over a few too many details though ;-)
Okay, I'll take a stab at this and see if I can convert DRM panel to it.
Thierry
On Mon, Nov 3, 2014 at 1:31 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Fri, Oct 31, 2014 at 04:51:43PM +0100, Daniel Vetter wrote:
On Wed, Oct 29, 2014 at 10:14:37AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 09:57:02AM +0100, Daniel Vetter wrote:
That makes the entire thing a bit non-trivial, which is why I think it would be better as some generic helper. Which then gets embedded or instantiated for specific cases, like dt&drm_panel or dt&drm_bridge. Or maybe even acpi&drm_bridge, who knows ;-)
I worry a little about type safety. How will this generic helper know what registry to look in for? Or conversely, if all these resources are added to a single registry how do you know that they're of the correct type? Failing to ensure this could cause situations where you're asking for a panel and get a bridge in return because you've wrongly wired it up in device tree for example.
But perhaps if both the registry and the device parts are turned into helpers we could still have a single core implementation and then instantiate that for each type, something roughly like this:
struct registry { struct list_head list; struct mutex lock; }; struct registry_record { struct list_head list; struct module *owner; struct kref *ref; struct device *dev; }; int registry_add(struct registry *registry, struct registry_record *record) { ... try_module_get(record->owner); ... } struct registry_record *registry_find_by_of_node(struct registry *registry, struct device_node *np) { ... kref_get(...); ... }
That way it should be possible to embed these into other structures, like so:
struct drm_panel { struct registry_record record; ... }; static struct registry drm_panels; int drm_panel_add(struct drm_panel *panel) { return registry_add(&drm_panels, &panel->record); } struct drm_panel *of_drm_panel_find(struct device_node *np) { struct registry_record *record; record = registry_find_by_of_node(&drm_panels, np); return container_of(record, struct drm_panel, record); }
Is that what you had in mind?
Yeah I've thought that we should instantiate using macros even, so that we have per-type registries. So you'd smash the usual set of DECLARE/DEFINE_AUX_DEV_REGISTRY into headers/source files, and they'd take a (name, key, value) tripled. For the example here(of_drm_panel, struct device_node *, struct drm_panel *) or similar. I might be hand-waving over a few too many details though ;-)
Okay, I'll take a stab at this and see if I can convert DRM panel to it.
It would be great if you can do this soon. I would anyhow need a reference for converting bridge framework as per Daniel's requirement :)
Ajay
On Mon, Oct 27, 2014 at 11:20:31PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
The struct device * is in DRM panel because there's nothing device tree specific about the concept. Having a struct device_node * instead would indicate that it can only be used with a device tree, whereas the framework doesn't care the tiniest bit what type of device we have.
While the trend clearly is to use more device tree, I don't think we should make it impossible for anybody else to use these frameworks.
There are other advantages to keeping a struct device *, like having access to the proper device and its name. Also you get access to the device_node * via dev->of_node anyway. I don't see any advantage in switching to just a struct device_node *, only disadvantages.
Thierry
On Tue, Oct 28, 2014 at 03:29:47PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:20:31PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
The struct device * is in DRM panel because there's nothing device tree specific about the concept. Having a struct device_node * instead would indicate that it can only be used with a device tree, whereas the framework doesn't care the tiniest bit what type of device we have.
While the trend clearly is to use more device tree, I don't think we should make it impossible for anybody else to use these frameworks.
There are other advantages to keeping a struct device *, like having access to the proper device and its name. Also you get access to the device_node * via dev->of_node anyway. I don't see any advantage in switching to just a struct device_node *, only disadvantages.
Well the idea is to make the lookup key specific, and conditional on #CONFIG_OF. If there's going to be another neat way to enumerate platform devices then I think we should add that, too. Or maybe we should have a void *platform_data or so.
The reason I really don't want a struct device * in core drm structures is that two releases down the road people will have found tons of really great ways to abuse them and re-create a midlayer. DRM core really should only care about the sw objects and not be hw specific at all. Heck there's not even an requirement to have any piece of actual hw, you could write a completely fake drm driver (for e.g. testing like the new v4l driver).
Tbh I wonder a bit why we even have this registery embedded into the core drm objects. Essentially the only thing you're doing is a list that maps some platform specific key onto some subsystem specific driver structure or fails the lookup. So instead of putting all these low-level details into drm core structures can't we just have a generic hashtable/list for this, plus some static inline helpers that cast the void * you get into the one you want?
I also get the feeling that this really should be in the driver core (like the component helpers), and that we should think a lot harder about lifetimes and refcounting (see my other reply on that). -Daniel
On Wed, Oct 29, 2014 at 08:51:27AM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 03:29:47PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:20:31PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
The struct device * is in DRM panel because there's nothing device tree specific about the concept. Having a struct device_node * instead would indicate that it can only be used with a device tree, whereas the framework doesn't care the tiniest bit what type of device we have.
While the trend clearly is to use more device tree, I don't think we should make it impossible for anybody else to use these frameworks.
There are other advantages to keeping a struct device *, like having access to the proper device and its name. Also you get access to the device_node * via dev->of_node anyway. I don't see any advantage in switching to just a struct device_node *, only disadvantages.
Well the idea is to make the lookup key specific, and conditional on #CONFIG_OF. If there's going to be another neat way to enumerate platform devices then I think we should add that, too. Or maybe we should have a void *platform_data or so.
The reason I really don't want a struct device * in core drm structures is that two releases down the road people will have found tons of really great ways to abuse them and re-create a midlayer. DRM core really should only care about the sw objects and not be hw specific at all. Heck there's not even an requirement to have any piece of actual hw, you could write a completely fake drm driver (for e.g. testing like the new v4l driver).
Tbh I wonder a bit why we even have this registery embedded into the core drm objects. Essentially the only thing you're doing is a list that maps some platform specific key onto some subsystem specific driver structure or fails the lookup. So instead of putting all these low-level details into drm core structures can't we just have a generic hashtable/list for this, plus some static inline helpers that cast the void * you get into the one you want?
I also get the feeling that this really should be in the driver core (like the component helpers), and that we should think a lot harder about lifetimes and refcounting (see my other reply on that).
Yes, that sounds very useful indeed. Also see my reply to yours. =)
Thierry
On Wed, Oct 29, 2014 at 10:16:49AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 08:51:27AM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 03:29:47PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:20:31PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
> @@ -660,8 +662,11 @@ struct drm_bridge_funcs { > * @driver_private: pointer to the bridge driver's internal context > */ > struct drm_bridge { > - struct drm_device *dev; > + struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
The struct device * is in DRM panel because there's nothing device tree specific about the concept. Having a struct device_node * instead would indicate that it can only be used with a device tree, whereas the framework doesn't care the tiniest bit what type of device we have.
While the trend clearly is to use more device tree, I don't think we should make it impossible for anybody else to use these frameworks.
There are other advantages to keeping a struct device *, like having access to the proper device and its name. Also you get access to the device_node * via dev->of_node anyway. I don't see any advantage in switching to just a struct device_node *, only disadvantages.
Well the idea is to make the lookup key specific, and conditional on #CONFIG_OF. If there's going to be another neat way to enumerate platform devices then I think we should add that, too. Or maybe we should have a void *platform_data or so.
The reason I really don't want a struct device * in core drm structures is that two releases down the road people will have found tons of really great ways to abuse them and re-create a midlayer. DRM core really should only care about the sw objects and not be hw specific at all. Heck there's not even an requirement to have any piece of actual hw, you could write a completely fake drm driver (for e.g. testing like the new v4l driver).
Tbh I wonder a bit why we even have this registery embedded into the core drm objects. Essentially the only thing you're doing is a list that maps some platform specific key onto some subsystem specific driver structure or fails the lookup. So instead of putting all these low-level details into drm core structures can't we just have a generic hashtable/list for this, plus some static inline helpers that cast the void * you get into the one you want?
I also get the feeling that this really should be in the driver core (like the component helpers), and that we should think a lot harder about lifetimes and refcounting (see my other reply on that).
Yes, that sounds very useful indeed. Also see my reply to yours. =)
Just replying here with some of the irc discussions we've had. Since drm_bridge/panel isn't a core drm interface object exposed to userspace it's less critical. I still think that wasting a few brain cycles to have a clear separation between the abstract interface object and how to bind and unbind the pieces together is worthwhile, even though the cost when getting it wrong is much less severe than in the case of a mandatory piece of core infrastructure.
I think in general the recent armsoc motivated drm infrastructure lacks a bit in attention to these details. E.g. the cma helpers are built into the drm.ko module, but clearly are auxilliary library code. So they should be pulled out and the headers clean up, so that we have a clear separation between core and helpers. Otherwise someone will sooner or later screw up and insert a helper depency into the core, and then we've started with the midlayer mess. Same goes with drm_bridge/panel, which didn't even bother to have separate headers from the core modeset header drm_crtc.h.
So would be great if someone could invest a bit of time into cleaning this up. Writing proper api docs also helps a lot with achieving a clean and sensible split ;-) -Daniel
On Fri, Oct 31, 2014 at 04:59:40PM +0100, Daniel Vetter wrote:
On Wed, Oct 29, 2014 at 10:16:49AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 08:51:27AM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 03:29:47PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:20:31PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote:
>> @@ -660,8 +662,11 @@ struct drm_bridge_funcs { >> * @driver_private: pointer to the bridge driver's internal context >> */ >> struct drm_bridge { >> - struct drm_device *dev; >> + struct device *dev; > > Please don't rename the ->dev pointer into drm. Because _all_ the other > drm structures still call it ->dev. Also, can't we use struct device_node > here like we do in the of helpers Russell added? See 7e435aad38083 >
I think this is modeled after the naming in drm_panel, FWIW. However, seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
The struct device * is in DRM panel because there's nothing device tree specific about the concept. Having a struct device_node * instead would indicate that it can only be used with a device tree, whereas the framework doesn't care the tiniest bit what type of device we have.
While the trend clearly is to use more device tree, I don't think we should make it impossible for anybody else to use these frameworks.
There are other advantages to keeping a struct device *, like having access to the proper device and its name. Also you get access to the device_node * via dev->of_node anyway. I don't see any advantage in switching to just a struct device_node *, only disadvantages.
Well the idea is to make the lookup key specific, and conditional on #CONFIG_OF. If there's going to be another neat way to enumerate platform devices then I think we should add that, too. Or maybe we should have a void *platform_data or so.
The reason I really don't want a struct device * in core drm structures is that two releases down the road people will have found tons of really great ways to abuse them and re-create a midlayer. DRM core really should only care about the sw objects and not be hw specific at all. Heck there's not even an requirement to have any piece of actual hw, you could write a completely fake drm driver (for e.g. testing like the new v4l driver).
Tbh I wonder a bit why we even have this registery embedded into the core drm objects. Essentially the only thing you're doing is a list that maps some platform specific key onto some subsystem specific driver structure or fails the lookup. So instead of putting all these low-level details into drm core structures can't we just have a generic hashtable/list for this, plus some static inline helpers that cast the void * you get into the one you want?
I also get the feeling that this really should be in the driver core (like the component helpers), and that we should think a lot harder about lifetimes and refcounting (see my other reply on that).
Yes, that sounds very useful indeed. Also see my reply to yours. =)
Just replying here with some of the irc discussions we've had. Since drm_bridge/panel isn't a core drm interface object exposed to userspace it's less critical. I still think that wasting a few brain cycles to have a clear separation between the abstract interface object and how to bind and unbind the pieces together is worthwhile, even though the cost when getting it wrong is much less severe than in the case of a mandatory piece of core infrastructure.
I think in general the recent armsoc motivated drm infrastructure lacks a bit in attention to these details. E.g. the cma helpers are built into the drm.ko module, but clearly are auxilliary library code. So they should be pulled out and the headers clean up, so that we have a clear separation between core and helpers. Otherwise someone will sooner or later screw up and insert a helper depency into the core, and then we've started with the midlayer mess. Same goes with drm_bridge/panel, which didn't even bother to have separate headers from the core modeset header drm_crtc.h.
DRM panel does have a separate header. It's still built into the core DRM module, but using a separate drm-$(CONFIG_DRM_PANEL) += drm_panel.o entry in the makefile. At the time it didn't seem worth to add a completely separate module given the size of the code and the overhead associated with having a separate module.
Do you still want me to split it off into a separate module to clarify that it isn't part of the core?
So would be great if someone could invest a bit of time into cleaning this up. Writing proper api docs also helps a lot with achieving a clean and sensible split ;-)
There's a bit of API documentation for panels, but I'll see if I can find some time to enhance it.
Thierry
On Mon, Nov 03, 2014 at 09:06:04AM +0100, Thierry Reding wrote:
On Fri, Oct 31, 2014 at 04:59:40PM +0100, Daniel Vetter wrote:
On Wed, Oct 29, 2014 at 10:16:49AM +0100, Thierry Reding wrote:
On Wed, Oct 29, 2014 at 08:51:27AM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 03:29:47PM +0100, Thierry Reding wrote:
On Mon, Oct 27, 2014 at 11:20:31PM +0100, Daniel Vetter wrote:
On Mon, Oct 27, 2014 at 8:58 PM, Sean Paul seanpaul@chromium.org wrote: >>> @@ -660,8 +662,11 @@ struct drm_bridge_funcs { >>> * @driver_private: pointer to the bridge driver's internal context >>> */ >>> struct drm_bridge { >>> - struct drm_device *dev; >>> + struct device *dev; >> >> Please don't rename the ->dev pointer into drm. Because _all_ the other >> drm structures still call it ->dev. Also, can't we use struct device_node >> here like we do in the of helpers Russell added? See 7e435aad38083 >> > > I think this is modeled after the naming in drm_panel, FWIW. However, > seems reasonable to keep the device_node instead.
Hm, indeed. Tbh I vote to rename drm_panel->drm to ->dev and like with drm_crtc drop the struct device and go directly to a struct device_node. Since we don't really need the sturct device, the only thing we care about is the of_node. For added bonus wrap an #ifdef CONFIG_OF around all the various struct device_node in drm_foo.h. Should be all fairly simple to pull off with cocci.
Thierry?
The struct device * is in DRM panel because there's nothing device tree specific about the concept. Having a struct device_node * instead would indicate that it can only be used with a device tree, whereas the framework doesn't care the tiniest bit what type of device we have.
While the trend clearly is to use more device tree, I don't think we should make it impossible for anybody else to use these frameworks.
There are other advantages to keeping a struct device *, like having access to the proper device and its name. Also you get access to the device_node * via dev->of_node anyway. I don't see any advantage in switching to just a struct device_node *, only disadvantages.
Well the idea is to make the lookup key specific, and conditional on #CONFIG_OF. If there's going to be another neat way to enumerate platform devices then I think we should add that, too. Or maybe we should have a void *platform_data or so.
The reason I really don't want a struct device * in core drm structures is that two releases down the road people will have found tons of really great ways to abuse them and re-create a midlayer. DRM core really should only care about the sw objects and not be hw specific at all. Heck there's not even an requirement to have any piece of actual hw, you could write a completely fake drm driver (for e.g. testing like the new v4l driver).
Tbh I wonder a bit why we even have this registery embedded into the core drm objects. Essentially the only thing you're doing is a list that maps some platform specific key onto some subsystem specific driver structure or fails the lookup. So instead of putting all these low-level details into drm core structures can't we just have a generic hashtable/list for this, plus some static inline helpers that cast the void * you get into the one you want?
I also get the feeling that this really should be in the driver core (like the component helpers), and that we should think a lot harder about lifetimes and refcounting (see my other reply on that).
Yes, that sounds very useful indeed. Also see my reply to yours. =)
Just replying here with some of the irc discussions we've had. Since drm_bridge/panel isn't a core drm interface object exposed to userspace it's less critical. I still think that wasting a few brain cycles to have a clear separation between the abstract interface object and how to bind and unbind the pieces together is worthwhile, even though the cost when getting it wrong is much less severe than in the case of a mandatory piece of core infrastructure.
I think in general the recent armsoc motivated drm infrastructure lacks a bit in attention to these details. E.g. the cma helpers are built into the drm.ko module, but clearly are auxilliary library code. So they should be pulled out and the headers clean up, so that we have a clear separation between core and helpers. Otherwise someone will sooner or later screw up and insert a helper depency into the core, and then we've started with the midlayer mess. Same goes with drm_bridge/panel, which didn't even bother to have separate headers from the core modeset header drm_crtc.h.
DRM panel does have a separate header. It's still built into the core DRM module, but using a separate drm-$(CONFIG_DRM_PANEL) += drm_panel.o entry in the makefile. At the time it didn't seem worth to add a completely separate module given the size of the code and the overhead associated with having a separate module.
Do you still want me to split it off into a separate module to clarify that it isn't part of the core?
Oh, it doesn't need to be a complete standalone module, smashing it into drm_kms_helper is imo totally ok. It's just to make really sure that helpers are helpers and there's never a depency from drm.ko to any optional helper code. Having a separate module for all the helper code helps a lot in ensure that. Also, everything a helper can do, a driver should be able to do too. Again a separate helper ensures that you haven't missed any EXPORT_SYMBOL. Which should then be a good reminder to update the kerneldoc ;-)
So would be great if someone could invest a bit of time into cleaning this up. Writing proper api docs also helps a lot with achieving a clean and sensible split ;-)
There's a bit of API documentation for panels, but I'll see if I can find some time to enhance it.
Imo pulling into the DocBook template is also important, since if you do that the 0-day tester will complain if the kerneldoc gets out of sync. Which does increases the changes of it staying up-to-date a lot.
Cheers, Daniel
Hi Daniel and Sean,
Thanks for the comments!
On Tue, Oct 28, 2014 at 1:28 AM, Sean Paul seanpaul@chromium.org wrote:
On Mon, Oct 27, 2014 at 3:01 PM, Daniel Vetter daniel@ffwll.ch wrote:
So don't ask why but I accidentally ended up in a branch looking at this patch and didn't like it. So very quick&grumpy review.
First, please make the patch subject more descriptive: I'd expect a helper function scaffolding like the various crtc/probe/dp ... helpers we already have. You instead add code to untangle the probe ordering. Please say so.
Sure. I will reword it properly.
More comments below.
On Wed, Aug 27, 2014 at 07:59:37PM +0530, Ajay Kumar wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
[snip]
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel,
Right, The entire rework is based on how drm_panel framework is structured.
FWIW. However, seems reasonable to keep the device_node instead.
Yes, its visible that just device_node would be sufficient. This will save us from renaming drm_device as well.
struct drm_device *drm;
struct drm_encoder *encoder;
This breaks bridge->bridge chaining (if we ever get there). It seems pretty much unused anyway except for an EBUSY check. Can't you use bridge->dev for that?
Yeah, I'd prefer to pass drm_device directly into drm_bridge_attach and leave it up to the caller to establish the proper chain.
Ok. I will use drm_device pointer directly instead of passing encoder pointer.
struct list_head head;
struct list_head list;
These lists need better names. I know that the "head" is really awful, especially since it's actually not the head of the list but just an element.
I think we can just rip bridge_list out of mode_config if we're going to keep track of bridges elsewhere. So we can nuke "head" and keep "list". This also means that bridge->destroy() goes away, but that's probably Ok if everything converts to the standalone driver model where we have driver->remove()
Sean
Great! Thierry actually mentioned about this once, and we have the confirmation now.
struct drm_mode_object base;
Aside: I've noticed all this trying to update the kerneldoc for struct drm_bridge, which just showed that this patch makes inconsistent changes. Trying to write kerneldoc is a really great way to come up with better interfaces imo.
Cheers, Daniel
I din't get this actually. You want me to create Doc../drm_bridge.txt or something similar?
Ajay
On Tue, Oct 28, 2014 at 10:21 AM, Ajay kumar ajaynumb@gmail.com wrote:
Hi Daniel and Sean,
Thanks for the comments!
On Tue, Oct 28, 2014 at 1:28 AM, Sean Paul seanpaul@chromium.org wrote:
On Mon, Oct 27, 2014 at 3:01 PM, Daniel Vetter daniel@ffwll.ch wrote:
So don't ask why but I accidentally ended up in a branch looking at this patch and didn't like it. So very quick&grumpy review.
First, please make the patch subject more descriptive: I'd expect a helper function scaffolding like the various crtc/probe/dp ... helpers we already have. You instead add code to untangle the probe ordering. Please say so.
Sure. I will reword it properly.
More comments below.
On Wed, Aug 27, 2014 at 07:59:37PM +0530, Ajay Kumar wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
[snip]
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel,
Right, The entire rework is based on how drm_panel framework is structured.
FWIW. However, seems reasonable to keep the device_node instead.
Yes, its visible that just device_node would be sufficient. This will save us from renaming drm_device as well.
struct drm_device *drm;
struct drm_encoder *encoder;
This breaks bridge->bridge chaining (if we ever get there). It seems pretty much unused anyway except for an EBUSY check. Can't you use bridge->dev for that?
Yeah, I'd prefer to pass drm_device directly into drm_bridge_attach and leave it up to the caller to establish the proper chain.
Ok. I will use drm_device pointer directly instead of passing encoder pointer.
Hm, if you do this can you pls also update drm_panel accordingly? It shouldn't be a lot of fuzz and would make things around drm+dt more consistent.
struct list_head head;
struct list_head list;
These lists need better names. I know that the "head" is really awful, especially since it's actually not the head of the list but just an element.
I think we can just rip bridge_list out of mode_config if we're going to keep track of bridges elsewhere. So we can nuke "head" and keep "list". This also means that bridge->destroy() goes away, but that's probably Ok if everything converts to the standalone driver model where we have driver->remove()
Sean
Great! Thierry actually mentioned about this once, and we have the confirmation now.
struct drm_mode_object base;
Aside: I've noticed all this trying to update the kerneldoc for struct drm_bridge, which just showed that this patch makes inconsistent changes. Trying to write kerneldoc is a really great way to come up with better interfaces imo.
Cheers, Daniel
I din't get this actually. You want me to create Doc../drm_bridge.txt or something similar?
If you want to document drm_bridge then I recomment to sprinkle proper kerneldoc over drm_bridge.c and pull it all into the drm DocBook template. That way all the drm documentation is in one place. I've done that for drm_crtc.h in an unrelated patch series (but based upon a branch with your patch here included) and there's struct drm_bridge* in there. Hence why I've noticed.
If you do kerneldoc/DocBook integration for drm_bridge it's probably best to also do it for drm_panel and use the opportunity to review/rework the interfaces a bit for consistency. E.g. move dt related stuff into drm_of.c and have that in a separate section in the docs. -Daniel
On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, Oct 28, 2014 at 10:21 AM, Ajay kumar ajaynumb@gmail.com wrote:
Hi Daniel and Sean,
Thanks for the comments!
On Tue, Oct 28, 2014 at 1:28 AM, Sean Paul seanpaul@chromium.org wrote:
On Mon, Oct 27, 2014 at 3:01 PM, Daniel Vetter daniel@ffwll.ch wrote:
So don't ask why but I accidentally ended up in a branch looking at this patch and didn't like it. So very quick&grumpy review.
First, please make the patch subject more descriptive: I'd expect a helper function scaffolding like the various crtc/probe/dp ... helpers we already have. You instead add code to untangle the probe ordering. Please say so.
Sure. I will reword it properly.
More comments below.
On Wed, Aug 27, 2014 at 07:59:37PM +0530, Ajay Kumar wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
[snip]
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel,
Right, The entire rework is based on how drm_panel framework is structured.
FWIW. However, seems reasonable to keep the device_node instead.
Yes, its visible that just device_node would be sufficient. This will save us from renaming drm_device as well.
struct drm_device *drm;
struct drm_encoder *encoder;
This breaks bridge->bridge chaining (if we ever get there). It seems pretty much unused anyway except for an EBUSY check. Can't you use bridge->dev for that?
Yeah, I'd prefer to pass drm_device directly into drm_bridge_attach and leave it up to the caller to establish the proper chain.
Ok. I will use drm_device pointer directly instead of passing encoder pointer.
Hm, if you do this can you pls also update drm_panel accordingly? It shouldn't be a lot of fuzz and would make things around drm+dt more consistent.
Are you talking about using struct device_node instead of struct device? I guess you have misplaced the comment under the wrong section!
struct list_head head;
struct list_head list;
These lists need better names. I know that the "head" is really awful, especially since it's actually not the head of the list but just an element.
I think we can just rip bridge_list out of mode_config if we're going to keep track of bridges elsewhere. So we can nuke "head" and keep "list". This also means that bridge->destroy() goes away, but that's probably Ok if everything converts to the standalone driver model where we have driver->remove()
Sean
Great! Thierry actually mentioned about this once, and we have the confirmation now.
struct drm_mode_object base;
Aside: I've noticed all this trying to update the kerneldoc for struct drm_bridge, which just showed that this patch makes inconsistent changes. Trying to write kerneldoc is a really great way to come up with better interfaces imo.
Cheers, Daniel
I din't get this actually. You want me to create Doc../drm_bridge.txt or something similar?
If you want to document drm_bridge then I recomment to sprinkle proper kerneldoc over drm_bridge.c and pull it all into the drm DocBook template. That way all the drm documentation is in one place. I've done that for drm_crtc.h in an unrelated patch series (but based upon a branch with your patch here included) and there's struct drm_bridge* in there. Hence why I've noticed.
Can you send a link for that? And, is there any problem if the doc comes later?
Ajay
On Tue, Oct 28, 2014 at 1:28 PM, Ajay kumar ajaynumb@gmail.com wrote:
On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, Oct 28, 2014 at 10:21 AM, Ajay kumar ajaynumb@gmail.com wrote:
Hi Daniel and Sean,
Thanks for the comments!
On Tue, Oct 28, 2014 at 1:28 AM, Sean Paul seanpaul@chromium.org wrote:
On Mon, Oct 27, 2014 at 3:01 PM, Daniel Vetter daniel@ffwll.ch wrote:
So don't ask why but I accidentally ended up in a branch looking at this patch and didn't like it. So very quick&grumpy review.
First, please make the patch subject more descriptive: I'd expect a helper function scaffolding like the various crtc/probe/dp ... helpers we already have. You instead add code to untangle the probe ordering. Please say so.
Sure. I will reword it properly.
More comments below.
On Wed, Aug 27, 2014 at 07:59:37PM +0530, Ajay Kumar wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
[snip]
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel,
Right, The entire rework is based on how drm_panel framework is structured.
FWIW. However, seems reasonable to keep the device_node instead.
Yes, its visible that just device_node would be sufficient. This will save us from renaming drm_device as well.
struct drm_device *drm;
struct drm_encoder *encoder;
This breaks bridge->bridge chaining (if we ever get there). It seems pretty much unused anyway except for an EBUSY check. Can't you use bridge->dev for that?
Yeah, I'd prefer to pass drm_device directly into drm_bridge_attach and leave it up to the caller to establish the proper chain.
Ok. I will use drm_device pointer directly instead of passing encoder pointer.
Hm, if you do this can you pls also update drm_panel accordingly? It shouldn't be a lot of fuzz and would make things around drm+dt more consistent.
Are you talking about using struct device_node instead of struct device? I guess you have misplaced the comment under the wrong section!
Yeah, that should have been one up ;-)
struct list_head head;
struct list_head list;
These lists need better names. I know that the "head" is really awful, especially since it's actually not the head of the list but just an element.
I think we can just rip bridge_list out of mode_config if we're going to keep track of bridges elsewhere. So we can nuke "head" and keep "list". This also means that bridge->destroy() goes away, but that's probably Ok if everything converts to the standalone driver model where we have driver->remove()
Sean
Great! Thierry actually mentioned about this once, and we have the confirmation now.
struct drm_mode_object base;
Aside: I've noticed all this trying to update the kerneldoc for struct drm_bridge, which just showed that this patch makes inconsistent changes. Trying to write kerneldoc is a really great way to come up with better interfaces imo.
Cheers, Daniel
I din't get this actually. You want me to create Doc../drm_bridge.txt or something similar?
If you want to document drm_bridge then I recomment to sprinkle proper kerneldoc over drm_bridge.c and pull it all into the drm DocBook template. That way all the drm documentation is in one place. I've done that for drm_crtc.h in an unrelated patch series (but based upon a branch with your patch here included) and there's struct drm_bridge* in there. Hence why I've noticed.
Can you send a link for that? And, is there any problem if the doc comes later?
Since quite a while we've asked for the kerneldoc polish as part of each drm core patch series. It's just that drm_bridge/panel kinda have flown under the radar of the people usually asking for docs ;-) -Daniel
On Tue, Oct 28, 2014 at 10:19 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, Oct 28, 2014 at 1:28 PM, Ajay kumar ajaynumb@gmail.com wrote:
On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Tue, Oct 28, 2014 at 10:21 AM, Ajay kumar ajaynumb@gmail.com wrote:
Hi Daniel and Sean,
Thanks for the comments!
On Tue, Oct 28, 2014 at 1:28 AM, Sean Paul seanpaul@chromium.org wrote:
On Mon, Oct 27, 2014 at 3:01 PM, Daniel Vetter daniel@ffwll.ch wrote:
So don't ask why but I accidentally ended up in a branch looking at this patch and didn't like it. So very quick&grumpy review.
First, please make the patch subject more descriptive: I'd expect a helper function scaffolding like the various crtc/probe/dp ... helpers we already have. You instead add code to untangle the probe ordering. Please say so.
Sure. I will reword it properly.
More comments below.
On Wed, Aug 27, 2014 at 07:59:37PM +0530, Ajay Kumar wrote: > A set of helper functions are defined in this patch to make > bridge driver probe independent of the drm flow. > > The bridge devices register themselves on a lookup table > when they get probed by calling "drm_bridge_add". > > The parent encoder driver waits till the bridge is available > in the lookup table(by calling "of_drm_find_bridge") and then > continues with its initialization. > > The encoder driver should also call "drm_bridge_attach" to pass > on the drm_device, encoder pointers to the bridge object. > > drm_bridge_attach inturn calls drm_bridge_init to register itself > with the drm core. Later, it calls "bridge->funcs->attach" so that > bridge can continue with other initializations. > > Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
[snip]
> @@ -660,8 +662,11 @@ struct drm_bridge_funcs { > * @driver_private: pointer to the bridge driver's internal context > */ > struct drm_bridge { > - struct drm_device *dev; > + struct device *dev;
Please don't rename the ->dev pointer into drm. Because _all_ the other drm structures still call it ->dev. Also, can't we use struct device_node here like we do in the of helpers Russell added? See 7e435aad38083
I think this is modeled after the naming in drm_panel,
Right, The entire rework is based on how drm_panel framework is structured.
FWIW. However, seems reasonable to keep the device_node instead.
Yes, its visible that just device_node would be sufficient. This will save us from renaming drm_device as well.
> + struct drm_device *drm; > + struct drm_encoder *encoder;
This breaks bridge->bridge chaining (if we ever get there). It seems pretty much unused anyway except for an EBUSY check. Can't you use bridge->dev for that?
Yeah, I'd prefer to pass drm_device directly into drm_bridge_attach and leave it up to the caller to establish the proper chain.
Ok. I will use drm_device pointer directly instead of passing encoder pointer.
Hm, if you do this can you pls also update drm_panel accordingly? It shouldn't be a lot of fuzz and would make things around drm+dt more consistent.
Are you talking about using struct device_node instead of struct device? I guess you have misplaced the comment under the wrong section!
Yeah, that should have been one up ;-)
> struct list_head head; > + struct list_head list;
These lists need better names. I know that the "head" is really awful, especially since it's actually not the head of the list but just an element.
I think we can just rip bridge_list out of mode_config if we're going to keep track of bridges elsewhere. So we can nuke "head" and keep "list". This also means that bridge->destroy() goes away, but that's probably Ok if everything converts to the standalone driver model where we have driver->remove()
Sean
Great! Thierry actually mentioned about this once, and we have the confirmation now.
> > struct drm_mode_object base;
Aside: I've noticed all this trying to update the kerneldoc for struct drm_bridge, which just showed that this patch makes inconsistent changes. Trying to write kerneldoc is a really great way to come up with better interfaces imo.
Cheers, Daniel
I din't get this actually. You want me to create Doc../drm_bridge.txt or something similar?
If you want to document drm_bridge then I recomment to sprinkle proper kerneldoc over drm_bridge.c and pull it all into the drm DocBook template. That way all the drm documentation is in one place. I've done that for drm_crtc.h in an unrelated patch series (but based upon a branch with your patch here included) and there's struct drm_bridge* in there. Hence why I've noticed.
Can you send a link for that? And, is there any problem if the doc comes later?
Since quite a while we've asked for the kerneldoc polish as part of each drm core patch series. It's just that drm_bridge/panel kinda have flown under the radar of the people usually asking for docs ;-)
Heh, sorry about that.
Sean
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Tue, Oct 28, 2014 at 03:19:36PM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 1:28 PM, Ajay kumar ajaynumb@gmail.com wrote:
On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
[...]
Hm, if you do this can you pls also update drm_panel accordingly? It shouldn't be a lot of fuzz and would make things around drm+dt more consistent.
Are you talking about using struct device_node instead of struct device? I guess you have misplaced the comment under the wrong section!
Yeah, that should have been one up ;-)
Like I said earlier, I don't think dropping struct device * in favour of struct device_node * is a good idea.
If you want to document drm_bridge then I recomment to sprinkle proper kerneldoc over drm_bridge.c and pull it all into the drm DocBook template. That way all the drm documentation is in one place. I've done that for drm_crtc.h in an unrelated patch series (but based upon a branch with your patch here included) and there's struct drm_bridge* in there. Hence why I've noticed.
Can you send a link for that? And, is there any problem if the doc comes later?
Since quite a while we've asked for the kerneldoc polish as part of each drm core patch series. It's just that drm_bridge/panel kinda have flown under the radar of the people usually asking for docs ;-)
FWIW, there's some kerneldoc in include/drm/drm_panel.h but I guess I could write up something more complete and integrate it into DocBook.
Thierry
On Tue, Oct 28, 2014 at 8:11 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Oct 28, 2014 at 03:19:36PM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 1:28 PM, Ajay kumar ajaynumb@gmail.com wrote:
On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
[...]
Hm, if you do this can you pls also update drm_panel accordingly? It shouldn't be a lot of fuzz and would make things around drm+dt more consistent.
Are you talking about using struct device_node instead of struct device? I guess you have misplaced the comment under the wrong section!
Yeah, that should have been one up ;-)
Like I said earlier, I don't think dropping struct device * in favour of struct device_node * is a good idea.
I am not sure about drm_panel. But, I am not really doing anything with the struct device pointer in case of bridge. So, just wondering if it is really needed?
Ajay
On Tue, Oct 28, 2014 at 08:16:44PM +0530, Ajay kumar wrote:
On Tue, Oct 28, 2014 at 8:11 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Oct 28, 2014 at 03:19:36PM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 1:28 PM, Ajay kumar ajaynumb@gmail.com wrote:
On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
[...]
Hm, if you do this can you pls also update drm_panel accordingly? It shouldn't be a lot of fuzz and would make things around drm+dt more consistent.
Are you talking about using struct device_node instead of struct device? I guess you have misplaced the comment under the wrong section!
Yeah, that should have been one up ;-)
Like I said earlier, I don't think dropping struct device * in favour of struct device_node * is a good idea.
I am not sure about drm_panel. But, I am not really doing anything with the struct device pointer in case of bridge. So, just wondering if it is really needed?
I think it's useful to have it just to send the right message. DRM panel and DRM bridge aren't specific to device tree. They are completely generic and can work with any type of device, whether it was instantiated from the device tree or some other infrastructure. Dropping struct device * will make it effectively useless on anything but DT. I don't think we should strive for that, even if only DT-enabled platforms currently use them.
Thierry
On Tue, Oct 28, 2014 at 04:05:34PM +0100, Thierry Reding wrote:
On Tue, Oct 28, 2014 at 08:16:44PM +0530, Ajay kumar wrote:
On Tue, Oct 28, 2014 at 8:11 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Oct 28, 2014 at 03:19:36PM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 1:28 PM, Ajay kumar ajaynumb@gmail.com wrote:
On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
[...]
Hm, if you do this can you pls also update drm_panel accordingly? It shouldn't be a lot of fuzz and would make things around drm+dt more consistent.
Are you talking about using struct device_node instead of struct device? I guess you have misplaced the comment under the wrong section!
Yeah, that should have been one up ;-)
Like I said earlier, I don't think dropping struct device * in favour of struct device_node * is a good idea.
I am not sure about drm_panel. But, I am not really doing anything with the struct device pointer in case of bridge. So, just wondering if it is really needed?
I think it's useful to have it just to send the right message. DRM panel and DRM bridge aren't specific to device tree. They are completely generic and can work with any type of device, whether it was instantiated from the device tree or some other infrastructure. Dropping struct device * will make it effectively useless on anything but DT. I don't think we should strive for that, even if only DT-enabled platforms currently use them.
See my other reply, but I now think we should put neither into drm structures. This "find me the driver structures for this device" problem looks really generic, and it has nothing to do with the drm structures and concepts like bridges/panels at all. It shouldn't be in there at all.
Adding it looks very much like reintroducing the drm midlayer that we just finally made obsolete, just not at the top-level (struct drm_device) but at a bunch of leaf nodes. I expect all the same issues though. And I'm definitely not looking to de-midlayer more stuff that we're just adding.
Imo this should be solved as a separate helper thing, maybe in the driver core akin to the component helpers from Russell. -Daniel
On 10/29/2014 08:58 AM, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 04:05:34PM +0100, Thierry Reding wrote:
On Tue, Oct 28, 2014 at 08:16:44PM +0530, Ajay kumar wrote:
On Tue, Oct 28, 2014 at 8:11 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Oct 28, 2014 at 03:19:36PM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 1:28 PM, Ajay kumar ajaynumb@gmail.com wrote:
On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
[...]
> Hm, if you do this can you pls also update drm_panel accordingly? It > shouldn't be a lot of fuzz and would make things around drm+dt more > consistent. Are you talking about using struct device_node instead of struct device? I guess you have misplaced the comment under the wrong section!
Yeah, that should have been one up ;-)
Like I said earlier, I don't think dropping struct device * in favour of struct device_node * is a good idea.
I am not sure about drm_panel. But, I am not really doing anything with the struct device pointer in case of bridge. So, just wondering if it is really needed?
I think it's useful to have it just to send the right message. DRM panel and DRM bridge aren't specific to device tree. They are completely generic and can work with any type of device, whether it was instantiated from the device tree or some other infrastructure. Dropping struct device * will make it effectively useless on anything but DT. I don't think we should strive for that, even if only DT-enabled platforms currently use them.
See my other reply, but I now think we should put neither into drm structures. This "find me the driver structures for this device" problem looks really generic, and it has nothing to do with the drm structures and concepts like bridges/panels at all. It shouldn't be in there at all.
Adding it looks very much like reintroducing the drm midlayer that we just finally made obsolete, just not at the top-level (struct drm_device) but at a bunch of leaf nodes. I expect all the same issues though. And I'm definitely not looking to de-midlayer more stuff that we're just adding.
Imo this should be solved as a separate helper thing, maybe in the driver core akin to the component helpers from Russell. -Daniel
As I understand you want something generic to look for panels, bridges, whatever and, like components, it should allow to safe hot plug/unplug. I have proposed such thing few months ago [1]. Have you looked at it?
[1]: https://lkml.org/lkml/2014/4/30/345
Regards Andrzej
On Wed, Oct 29, 2014 at 10:09:04AM +0100, Andrzej Hajda wrote:
On 10/29/2014 08:58 AM, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 04:05:34PM +0100, Thierry Reding wrote:
On Tue, Oct 28, 2014 at 08:16:44PM +0530, Ajay kumar wrote:
On Tue, Oct 28, 2014 at 8:11 PM, Thierry Reding thierry.reding@gmail.com wrote:
On Tue, Oct 28, 2014 at 03:19:36PM +0100, Daniel Vetter wrote:
On Tue, Oct 28, 2014 at 1:28 PM, Ajay kumar ajaynumb@gmail.com wrote: > On Tue, Oct 28, 2014 at 3:31 PM, Daniel Vetter daniel@ffwll.ch wrote:
[...]
>> Hm, if you do this can you pls also update drm_panel accordingly? It >> shouldn't be a lot of fuzz and would make things around drm+dt more >> consistent. > Are you talking about using struct device_node instead of struct device? > I guess you have misplaced the comment under the wrong section!
Yeah, that should have been one up ;-)
Like I said earlier, I don't think dropping struct device * in favour of struct device_node * is a good idea.
I am not sure about drm_panel. But, I am not really doing anything with the struct device pointer in case of bridge. So, just wondering if it is really needed?
I think it's useful to have it just to send the right message. DRM panel and DRM bridge aren't specific to device tree. They are completely generic and can work with any type of device, whether it was instantiated from the device tree or some other infrastructure. Dropping struct device * will make it effectively useless on anything but DT. I don't think we should strive for that, even if only DT-enabled platforms currently use them.
See my other reply, but I now think we should put neither into drm structures. This "find me the driver structures for this device" problem looks really generic, and it has nothing to do with the drm structures and concepts like bridges/panels at all. It shouldn't be in there at all.
Adding it looks very much like reintroducing the drm midlayer that we just finally made obsolete, just not at the top-level (struct drm_device) but at a bunch of leaf nodes. I expect all the same issues though. And I'm definitely not looking to de-midlayer more stuff that we're just adding.
Imo this should be solved as a separate helper thing, maybe in the driver core akin to the component helpers from Russell. -Daniel
As I understand you want something generic to look for panels, bridges, whatever and, like components, it should allow to safe hot plug/unplug. I have proposed such thing few months ago [1]. Have you looked at it?
Yeah I think I've looked but wasn't convinced. I do agree though that we should definitely aim for something in the driver core. Since if Greg doesn't like it it's not suddenly better if we just hide it in the drm subsystem. This really smells like a generic issue - after all we already have a two implementations with bridges&panels already.
So maybe we need to augment the component framework with the possibility to add additional devices later on at runtime, or something similar. Not really sure since I don't have actual practice with these issues since i915 doesn't (yet) have these problems. -Daniel
On Wed, Aug 27, 2014 at 10:29 AM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 15 ++++- drivers/gpu/drm/drm_bridge.c | 102 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc.c | 4 +- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 4 +- include/drm/drm_crtc.h | 12 +++- 6 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/drm_bridge.c
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4a55d59..bdbfb6f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -19,6 +19,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o +drm-$(CONFIG_DRM_BRIDGE) += drm_bridge.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 884923f..5a8e907 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -1,5 +1,16 @@ -config DRM_PTN3460
tristate "PTN3460 DP/LVDS bridge"
+config DRM_BRIDGE
I'm not convinced this adds any value, to be honest. In addition to whether or not it's useful, it seems like you'd need to stub the drm_bridge_* functions that are declared in drm_crtc.h or break them out into drm_bridge.h.
Sean
tristate depends on DRM select DRM_KMS_HELPER
help
Bridge registration and lookup framework.
+menu "bridge chips"
depends on DRM_BRIDGE
+config DRM_PTN3460
tristate "PTN3460 DP/LVDS bridge"
depends on DRM_BRIDGE ---help---
+endmenu diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c new file mode 100644 index 0000000..b2d43fd --- /dev/null +++ b/drivers/gpu/drm/drm_bridge.c @@ -0,0 +1,102 @@ +/*
- Copyright (c) 2014 Samsung Electronics Co., Ltd
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sub license,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice (including the
- next paragraph) shall be included in all copies or substantial portions
- of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
- */
+#include <linux/err.h> +#include <linux/module.h>
+#include <drm/drm_crtc.h>
+#include "drm/drmP.h"
+static DEFINE_MUTEX(bridge_lock); +static LIST_HEAD(bridge_list);
+int drm_bridge_add(struct drm_bridge *bridge) +{
mutex_lock(&bridge_lock);
list_add_tail(&bridge->list, &bridge_list);
mutex_unlock(&bridge_lock);
return 0;
+} +EXPORT_SYMBOL(drm_bridge_add);
+void drm_bridge_remove(struct drm_bridge *bridge) +{
mutex_lock(&bridge_lock);
list_del_init(&bridge->list);
mutex_unlock(&bridge_lock);
+} +EXPORT_SYMBOL(drm_bridge_remove);
+int drm_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder)
+{
int ret;
if (!bridge || !encoder)
return -EINVAL;
if (bridge->encoder)
return -EBUSY;
encoder->bridge = bridge;
bridge->encoder = encoder;
bridge->drm = encoder->dev;
ret = drm_bridge_init(bridge->drm, bridge);
if (ret) {
DRM_ERROR("Failed to register bridge with drm\n");
return ret;
}
if (bridge->funcs->attach)
return bridge->funcs->attach(bridge);
return 0;
+} +EXPORT_SYMBOL(drm_bridge_attach);
+#ifdef CONFIG_OF +struct drm_bridge *of_drm_find_bridge(struct device_node *np) +{
struct drm_bridge *bridge;
mutex_lock(&bridge_lock);
list_for_each_entry(bridge, &bridge_list, list) {
if (bridge->dev->of_node == np) {
mutex_unlock(&bridge_lock);
return bridge;
}
}
mutex_unlock(&bridge_lock);
return NULL;
+} +EXPORT_SYMBOL(of_drm_find_bridge); +#endif
+MODULE_AUTHOR("Ajay Kumar ajaykumar.rs@samsung.com"); +MODULE_DESCRIPTION("DRM bridge infrastructure"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 25f5cfa..2fb22fa 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1039,7 +1039,7 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge) if (ret) goto out;
bridge->dev = dev;
bridge->drm = dev; list_add_tail(&bridge->head, &dev->mode_config.bridge_list); dev->mode_config.num_bridge++;
@@ -1058,7 +1058,7 @@ EXPORT_SYMBOL(drm_bridge_init); */ void drm_bridge_cleanup(struct drm_bridge *bridge) {
struct drm_device *dev = bridge->dev;
struct drm_device *dev = bridge->drm; drm_modeset_lock_all(dev); drm_mode_object_put(dev, &bridge->base);
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 0309539..bc9e5ff 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -33,7 +33,7 @@ static void hdmi_bridge_destroy(struct drm_bridge *bridge)
static void power_on(struct drm_bridge *bridge) {
struct drm_device *dev = bridge->dev;
struct drm_device *dev = bridge->drm; struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; const struct hdmi_platform_config *config = hdmi->config;
@@ -67,7 +67,7 @@ static void power_on(struct drm_bridge *bridge)
static void power_off(struct drm_bridge *bridge) {
struct drm_device *dev = bridge->dev;
struct drm_device *dev = bridge->drm; struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; const struct hdmi_platform_config *config = hdmi->config;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f48a436..f6f426f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -629,6 +629,7 @@ struct drm_plane {
/**
- drm_bridge_funcs - drm_bridge control functions
- @attach: Called during drm_bridge_attach
- @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
- @disable: Called right before encoder prepare, disables the bridge
- @post_disable: Called right after encoder prepare, for lockstepped disable
@@ -638,6 +639,7 @@ struct drm_plane {
- @destroy: make object go away
*/ struct drm_bridge_funcs {
int (*attach)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
struct drm_device *drm;
struct drm_encoder *encoder; struct list_head head;
struct list_head list; struct drm_mode_object base;
@@ -906,6 +911,11 @@ extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ extern void drm_connector_unplug_all(struct drm_device *dev);
+extern int drm_bridge_add(struct drm_bridge *bridge); +extern void drm_bridge_remove(struct drm_bridge *bridge); +extern struct drm_bridge *of_drm_find_bridge(struct device_node *np); +extern int drm_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder);
extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge); extern void drm_bridge_cleanup(struct drm_bridge *bridge);
-- 1.7.9.5
-- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Oct 28, 2014 at 1:41 AM, Sean Paul seanpaul@chromium.org wrote:
On Wed, Aug 27, 2014 at 10:29 AM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 15 ++++- drivers/gpu/drm/drm_bridge.c | 102 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc.c | 4 +- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 4 +- include/drm/drm_crtc.h | 12 +++- 6 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/drm_bridge.c
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4a55d59..bdbfb6f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -19,6 +19,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o +drm-$(CONFIG_DRM_BRIDGE) += drm_bridge.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 884923f..5a8e907 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -1,5 +1,16 @@ -config DRM_PTN3460
tristate "PTN3460 DP/LVDS bridge"
+config DRM_BRIDGE
I'm not convinced this adds any value, to be honest. In addition to whether or not it's useful, it seems like you'd need to stub the drm_bridge_* functions that are declared in drm_crtc.h or break them out into drm_bridge.h.
Sean
tristate depends on DRM select DRM_KMS_HELPER
help
Bridge registration and lookup framework.
+menu "bridge chips"
depends on DRM_BRIDGE
+config DRM_PTN3460
tristate "PTN3460 DP/LVDS bridge"
depends on DRM_BRIDGE ---help---
+endmenu diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c new file mode 100644 index 0000000..b2d43fd --- /dev/null +++ b/drivers/gpu/drm/drm_bridge.c @@ -0,0 +1,102 @@ +/*
- Copyright (c) 2014 Samsung Electronics Co., Ltd
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sub license,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice (including the
- next paragraph) shall be included in all copies or substantial portions
- of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
- */
+#include <linux/err.h> +#include <linux/module.h>
+#include <drm/drm_crtc.h>
+#include "drm/drmP.h"
+static DEFINE_MUTEX(bridge_lock); +static LIST_HEAD(bridge_list);
+int drm_bridge_add(struct drm_bridge *bridge) +{
mutex_lock(&bridge_lock);
list_add_tail(&bridge->list, &bridge_list);
mutex_unlock(&bridge_lock);
return 0;
+} +EXPORT_SYMBOL(drm_bridge_add);
+void drm_bridge_remove(struct drm_bridge *bridge) +{
mutex_lock(&bridge_lock);
list_del_init(&bridge->list);
mutex_unlock(&bridge_lock);
+} +EXPORT_SYMBOL(drm_bridge_remove);
+int drm_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder)
+{
int ret;
if (!bridge || !encoder)
return -EINVAL;
if (bridge->encoder)
return -EBUSY;
encoder->bridge = bridge;
bridge->encoder = encoder;
bridge->drm = encoder->dev;
ret = drm_bridge_init(bridge->drm, bridge);
if (ret) {
DRM_ERROR("Failed to register bridge with drm\n");
return ret;
}
if (bridge->funcs->attach)
return bridge->funcs->attach(bridge);
return 0;
+} +EXPORT_SYMBOL(drm_bridge_attach);
+#ifdef CONFIG_OF +struct drm_bridge *of_drm_find_bridge(struct device_node *np) +{
struct drm_bridge *bridge;
mutex_lock(&bridge_lock);
list_for_each_entry(bridge, &bridge_list, list) {
if (bridge->dev->of_node == np) {
mutex_unlock(&bridge_lock);
return bridge;
}
}
mutex_unlock(&bridge_lock);
return NULL;
+} +EXPORT_SYMBOL(of_drm_find_bridge); +#endif
+MODULE_AUTHOR("Ajay Kumar ajaykumar.rs@samsung.com"); +MODULE_DESCRIPTION("DRM bridge infrastructure"); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 25f5cfa..2fb22fa 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1039,7 +1039,7 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge) if (ret) goto out;
bridge->dev = dev;
bridge->drm = dev; list_add_tail(&bridge->head, &dev->mode_config.bridge_list); dev->mode_config.num_bridge++;
@@ -1058,7 +1058,7 @@ EXPORT_SYMBOL(drm_bridge_init); */ void drm_bridge_cleanup(struct drm_bridge *bridge) {
struct drm_device *dev = bridge->dev;
struct drm_device *dev = bridge->drm; drm_modeset_lock_all(dev); drm_mode_object_put(dev, &bridge->base);
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 0309539..bc9e5ff 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -33,7 +33,7 @@ static void hdmi_bridge_destroy(struct drm_bridge *bridge)
static void power_on(struct drm_bridge *bridge) {
struct drm_device *dev = bridge->dev;
struct drm_device *dev = bridge->drm; struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; const struct hdmi_platform_config *config = hdmi->config;
@@ -67,7 +67,7 @@ static void power_on(struct drm_bridge *bridge)
static void power_off(struct drm_bridge *bridge) {
struct drm_device *dev = bridge->dev;
struct drm_device *dev = bridge->drm; struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; const struct hdmi_platform_config *config = hdmi->config;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f48a436..f6f426f 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -629,6 +629,7 @@ struct drm_plane {
/**
- drm_bridge_funcs - drm_bridge control functions
- @attach: Called during drm_bridge_attach
- @mode_fixup: Try to fixup (or reject entirely) proposed mode for this bridge
- @disable: Called right before encoder prepare, disables the bridge
- @post_disable: Called right after encoder prepare, for lockstepped disable
@@ -638,6 +639,7 @@ struct drm_plane {
- @destroy: make object go away
*/ struct drm_bridge_funcs {
int (*attach)(struct drm_bridge *bridge); bool (*mode_fixup)(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
@@ -660,8 +662,11 @@ struct drm_bridge_funcs {
- @driver_private: pointer to the bridge driver's internal context
*/ struct drm_bridge {
struct drm_device *dev;
struct device *dev;
struct drm_device *drm;
struct drm_encoder *encoder; struct list_head head;
struct list_head list; struct drm_mode_object base;
@@ -906,6 +911,11 @@ extern void drm_connector_cleanup(struct drm_connector *connector); /* helper to unplug all connectors from sysfs for device */ extern void drm_connector_unplug_all(struct drm_device *dev);
+extern int drm_bridge_add(struct drm_bridge *bridge); +extern void drm_bridge_remove(struct drm_bridge *bridge); +extern struct drm_bridge *of_drm_find_bridge(struct device_node *np); +extern int drm_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder);
extern int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge); extern void drm_bridge_cleanup(struct drm_bridge *bridge);
-- 1.7.9.5
-- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, Oct 28, 2014 at 1:41 AM, Sean Paul seanpaul@chromium.org wrote:
On Wed, Aug 27, 2014 at 10:29 AM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
A set of helper functions are defined in this patch to make bridge driver probe independent of the drm flow.
The bridge devices register themselves on a lookup table when they get probed by calling "drm_bridge_add".
The parent encoder driver waits till the bridge is available in the lookup table(by calling "of_drm_find_bridge") and then continues with its initialization.
The encoder driver should also call "drm_bridge_attach" to pass on the drm_device, encoder pointers to the bridge object.
drm_bridge_attach inturn calls drm_bridge_init to register itself with the drm core. Later, it calls "bridge->funcs->attach" so that bridge can continue with other initializations.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com
drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 15 ++++- drivers/gpu/drm/drm_bridge.c | 102 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc.c | 4 +- drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 4 +- include/drm/drm_crtc.h | 12 +++- 6 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/drm_bridge.c
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4a55d59..bdbfb6f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -19,6 +19,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o +drm-$(CONFIG_DRM_BRIDGE) += drm_bridge.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 884923f..5a8e907 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -1,5 +1,16 @@ -config DRM_PTN3460
tristate "PTN3460 DP/LVDS bridge"
+config DRM_BRIDGE
I'm not convinced this adds any value, to be honest.
Hmm, then how to compile drm_bridge.c?
In addition to whether or not it's useful, it seems like you'd need to stub the drm_bridge_* functions that are declared in drm_crtc.h or break them out into drm_bridge.h.
Well, Thierry mentioned about this long back. Again, we have the confirmation now!
Ajay [snip]
Use drm_bridge helpers to modify the driver to support i2c driver model.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/bridge/Kconfig | 6 +- drivers/gpu/drm/bridge/ptn3460.c | 122 +++++++++++++++++++++---------- drivers/gpu/drm/exynos/Kconfig | 2 +- drivers/gpu/drm/exynos/exynos_dp_core.c | 22 ------ 4 files changed, 89 insertions(+), 63 deletions(-)
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 5a8e907..7e63a52 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -10,7 +10,9 @@ menu "bridge chips"
config DRM_PTN3460 tristate "PTN3460 DP/LVDS bridge" - depends on DRM_BRIDGE - ---help--- + depends on OF + depends on I2C + help + ptn3460 eDP-LVDS bridge chip driver.
endmenu diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index a2ddc8d..651640a 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -36,7 +36,6 @@ struct ptn3460_bridge { struct drm_connector connector; struct i2c_client *client; - struct drm_encoder *encoder; struct drm_bridge bridge; struct edid *edid; int gpio_pd_n; @@ -188,14 +187,6 @@ static void ptn3460_bridge_destroy(struct drm_bridge *bridge) /* Nothing else to free, we've got devm allocated memory */ }
-static struct drm_bridge_funcs ptn3460_bridge_funcs = { - .pre_enable = ptn3460_pre_enable, - .enable = ptn3460_enable, - .disable = ptn3460_disable, - .post_disable = ptn3460_post_disable, - .destroy = ptn3460_bridge_destroy, -}; - static int ptn3460_get_modes(struct drm_connector *connector) { struct ptn3460_bridge *ptn_bridge; @@ -240,7 +231,7 @@ static struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) { struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector);
- return ptn_bridge->encoder; + return ptn_bridge->bridge.encoder; }
static struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { @@ -266,31 +257,62 @@ static struct drm_connector_funcs ptn3460_connector_funcs = { .destroy = ptn3460_connector_destroy, };
-int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, - struct i2c_client *client, struct device_node *node) +int ptn3460_bridge_attach(struct drm_bridge *bridge) { + struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); int ret; + + ret = drm_connector_init(bridge->drm, &ptn_bridge->connector, + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + drm_connector_helper_add(&ptn_bridge->connector, + &ptn3460_connector_helper_funcs); + drm_connector_register(&ptn_bridge->connector); + drm_mode_connector_attach_encoder(&ptn_bridge->connector, + bridge->encoder); + + return ret; +} + +static struct drm_bridge_funcs ptn3460_bridge_funcs = { + .pre_enable = ptn3460_pre_enable, + .enable = ptn3460_enable, + .disable = ptn3460_disable, + .post_disable = ptn3460_post_disable, + .destroy = ptn3460_bridge_destroy, + .attach = ptn3460_bridge_attach, +}; + +static int ptn3460_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; struct ptn3460_bridge *ptn_bridge; + int ret;
- ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL); + ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL); if (!ptn_bridge) { return -ENOMEM; }
ptn_bridge->client = client; - ptn_bridge->encoder = encoder; - ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0); + ptn_bridge->gpio_pd_n = of_get_named_gpio(dev->of_node, + "powerdown-gpio", 0); if (gpio_is_valid(ptn_bridge->gpio_pd_n)) { ret = gpio_request_one(ptn_bridge->gpio_pd_n, GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N"); if (ret) { - dev_err(&client->dev, - "Request powerdown-gpio failed (%d)\n", ret); + dev_err(dev, "Request powerdown-gpio failed (%d)\n", + ret); return ret; } }
- ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0); + ptn_bridge->gpio_rst_n = of_get_named_gpio(dev->of_node, + "reset-gpio", 0); if (gpio_is_valid(ptn_bridge->gpio_rst_n)) { /* * Request the reset pin low to avoid the bridge being @@ -299,42 +321,30 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, ret = gpio_request_one(ptn_bridge->gpio_rst_n, GPIOF_OUT_INIT_LOW, "PTN3460_RST_N"); if (ret) { - dev_err(&client->dev, - "Request reset-gpio failed (%d)\n", ret); + dev_err(dev, "Request reset-gpio failed (%d)\n", ret); gpio_free(ptn_bridge->gpio_pd_n); return ret; } }
- ret = of_property_read_u32(node, "edid-emulation", + ret = of_property_read_u32(dev->of_node, "edid-emulation", &ptn_bridge->edid_emulation); if (ret) { - dev_err(&client->dev, "Can't read EDID emulation value\n"); + dev_err(dev, "Can't read EDID emulation value\n"); goto err; }
+ ptn_bridge->bridge.dev = dev; ptn_bridge->bridge.funcs = &ptn3460_bridge_funcs; - ret = drm_bridge_init(dev, &ptn_bridge->bridge); + ret = drm_bridge_add(&ptn_bridge->bridge); if (ret) { - DRM_ERROR("Failed to initialize bridge with drm\n"); + DRM_ERROR("Failed to add bridge\n"); goto err; }
- encoder->bridge = &ptn_bridge->bridge; - - ret = drm_connector_init(dev, &ptn_bridge->connector, - &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS); - if (ret) { - DRM_ERROR("Failed to initialize connector with drm\n"); - goto err; - } - drm_connector_helper_add(&ptn_bridge->connector, - &ptn3460_connector_helper_funcs); - drm_connector_register(&ptn_bridge->connector); - drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder); + i2c_set_clientdata(client, ptn_bridge);
return 0; - err: if (gpio_is_valid(ptn_bridge->gpio_pd_n)) gpio_free(ptn_bridge->gpio_pd_n); @@ -342,4 +352,40 @@ err: gpio_free(ptn_bridge->gpio_rst_n); return ret; } -EXPORT_SYMBOL(ptn3460_init); + +static int ptn3460_remove(struct i2c_client *client) +{ + struct ptn3460_bridge *ptn_bridge = i2c_get_clientdata(client); + + drm_bridge_remove(&ptn_bridge->bridge); + + return 0; +} + +static const struct i2c_device_id ptn3460_i2c_table[] = { + {"nxp,ptn3460", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ptn3460_i2c_table); + +static const struct of_device_id ptn3460_match[] = { + { .compatible = "nxp,ptn3460" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ptn3460_match); + +static struct i2c_driver ptn3460_driver = { + .id_table = ptn3460_i2c_table, + .probe = ptn3460_probe, + .remove = ptn3460_remove, + .driver = { + .name = "nxp,ptn3460", + .owner = THIS_MODULE, + .of_match_table = ptn3460_match, + }, +}; +module_i2c_driver(ptn3460_driver); + +MODULE_AUTHOR("Sean Paul seanpaul@chromium.org"); +MODULE_DESCRIPTION("NXP ptn3460 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 7f9f6f9..4c0e071 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -51,7 +51,7 @@ config DRM_EXYNOS_DSI
config DRM_EXYNOS_DP bool "EXYNOS DRM DP driver support" - depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS && (DRM_PTN3460=n || DRM_PTN3460=y || DRM_PTN3460=DRM_EXYNOS) + depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS default DRM_EXYNOS select DRM_PANEL help diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 4f3c7eb..61ce6e4 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -986,32 +986,10 @@ static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { .best_encoder = exynos_dp_best_encoder, };
-static bool find_bridge(const char *compat, struct bridge_init *bridge) -{ - bridge->client = NULL; - bridge->node = of_find_compatible_node(NULL, NULL, compat); - if (!bridge->node) - return false; - - bridge->client = of_find_i2c_device_by_node(bridge->node); - if (!bridge->client) - return false; - - return true; -} - /* returns the number of bridges attached */ static int exynos_drm_attach_lcd_bridge(struct drm_device *dev, struct drm_encoder *encoder) { - struct bridge_init bridge; - int ret; - - if (find_bridge("nxp,ptn3460", &bridge)) { - ret = ptn3460_init(dev, encoder, bridge.client, bridge.node); - if (!ret) - return 1; - } return 0; }
Modify driver to support drm_bridge.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/exynos/Kconfig | 1 + drivers/gpu/drm/exynos/exynos_dp_core.c | 38 +++++++++++++++++++++++-------- drivers/gpu/drm/exynos/exynos_dp_core.h | 1 + 3 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 4c0e071..bdef294 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -54,6 +54,7 @@ config DRM_EXYNOS_DP depends on DRM_EXYNOS_FIMD && ARCH_EXYNOS default DRM_EXYNOS select DRM_PANEL + select DRM_BRIDGE help This enables support for DP device.
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 61ce6e4..a8a8b87 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -987,9 +987,17 @@ static struct drm_connector_helper_funcs exynos_dp_connector_helper_funcs = { };
/* returns the number of bridges attached */ -static int exynos_drm_attach_lcd_bridge(struct drm_device *dev, +static int exynos_drm_attach_lcd_bridge(struct exynos_dp_device *dp, struct drm_encoder *encoder) { + int ret; + + ret = drm_bridge_attach(dp->bridge, encoder); + if (ret) { + DRM_ERROR("Failed to attach bridge to encoder\n"); + return ret; + } + return 0; }
@@ -1003,9 +1011,11 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display, dp->encoder = encoder;
/* Pre-empt DP connector creation if there's a bridge */ - ret = exynos_drm_attach_lcd_bridge(dp->drm_dev, encoder); - if (ret) - return 0; + if (dp->bridge) { + ret = exynos_drm_attach_lcd_bridge(dp, encoder); + if (!ret) + return 0; + }
connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -1257,7 +1267,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data) if (ret) return ret;
- if (!dp->panel) { + if (!dp->panel && !dp->bridge) { ret = exynos_dp_dt_parse_panel(dp); if (ret) return ret; @@ -1348,7 +1358,7 @@ static const struct component_ops exynos_dp_ops = { static int exynos_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *panel_node; + struct device_node *node; struct exynos_dp_device *dp; int ret;
@@ -1362,14 +1372,22 @@ static int exynos_dp_probe(struct platform_device *pdev) if (!dp) return -ENOMEM;
- panel_node = of_parse_phandle(dev->of_node, "panel", 0); - if (panel_node) { - dp->panel = of_drm_find_panel(panel_node); - of_node_put(panel_node); + node = of_parse_phandle(dev->of_node, "panel", 0); + if (node) { + dp->panel = of_drm_find_panel(node); + of_node_put(node); if (!dp->panel) return -EPROBE_DEFER; }
+ node = of_parse_phandle(dev->of_node, "bridge", 0); + if (node) { + dp->bridge = of_drm_find_bridge(node); + of_node_put(node); + if (!dp->bridge) + return -EPROBE_DEFER; + } + exynos_dp_display.ctx = dp;
ret = component_add(&pdev->dev, &exynos_dp_ops); diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h index a1aee69..3b0ba93 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.h +++ b/drivers/gpu/drm/exynos/exynos_dp_core.h @@ -150,6 +150,7 @@ struct exynos_dp_device { struct drm_connector connector; struct drm_encoder *encoder; struct drm_panel *panel; + struct drm_bridge *bridge; struct clk *clock; unsigned int irq; void __iomem *reg_base;
Add drm_panel calls to the driver to make the panel and bridge work together in tandem.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/bridge/Kconfig | 1 + drivers/gpu/drm/bridge/ptn3460.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+)
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 7e63a52..d35fb68 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -12,6 +12,7 @@ config DRM_PTN3460 tristate "PTN3460 DP/LVDS bridge" depends on OF depends on I2C + select DRM_PANEL help ptn3460 eDP-LVDS bridge chip driver.
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index 651640a..3cb3a7c 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -20,6 +20,8 @@ #include <linux/of.h> #include <linux/of_gpio.h>
+#include <drm/drm_panel.h> + #include "bridge/ptn3460.h"
#include "drm_crtc.h" @@ -38,6 +40,7 @@ struct ptn3460_bridge { struct i2c_client *client; struct drm_bridge bridge; struct edid *edid; + struct drm_panel *panel; int gpio_pd_n; int gpio_rst_n; u32 edid_emulation; @@ -137,6 +140,11 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge) gpio_set_value(ptn_bridge->gpio_rst_n, 1); }
+ if (drm_panel_prepare(ptn_bridge->panel)) { + DRM_ERROR("failed to prepare panel\n"); + return; + } + /* * There's a bug in the PTN chip where it falsely asserts hotplug before * it is fully functional. We're forced to wait for the maximum start up @@ -153,6 +161,12 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge)
static void ptn3460_enable(struct drm_bridge *bridge) { + struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); + + if (drm_panel_enable(ptn_bridge->panel)) { + DRM_ERROR("failed to enable panel\n"); + return; + } }
static void ptn3460_disable(struct drm_bridge *bridge) @@ -164,6 +178,11 @@ static void ptn3460_disable(struct drm_bridge *bridge)
ptn_bridge->enabled = false;
+ if (drm_panel_disable(ptn_bridge->panel)) { + DRM_ERROR("failed to disable panel\n"); + return; + } + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) gpio_set_value(ptn_bridge->gpio_rst_n, 1);
@@ -173,6 +192,12 @@ static void ptn3460_disable(struct drm_bridge *bridge)
static void ptn3460_post_disable(struct drm_bridge *bridge) { + struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); + + if (drm_panel_unprepare(ptn_bridge->panel)) { + DRM_ERROR("failed to unprepare panel\n"); + return; + } }
static void ptn3460_bridge_destroy(struct drm_bridge *bridge) @@ -274,6 +299,9 @@ int ptn3460_bridge_attach(struct drm_bridge *bridge) drm_mode_connector_attach_encoder(&ptn_bridge->connector, bridge->encoder);
+ if (ptn_bridge->panel) + drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector); + return ret; }
@@ -291,6 +319,7 @@ static int ptn3460_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct ptn3460_bridge *ptn_bridge; + struct device_node *panel_node; int ret;
ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL); @@ -298,6 +327,14 @@ static int ptn3460_probe(struct i2c_client *client, return -ENOMEM; }
+ panel_node = of_parse_phandle(dev->of_node, "panel", 0); + if (panel_node) { + ptn_bridge->panel = of_drm_find_panel(panel_node); + of_node_put(panel_node); + if (!ptn_bridge->panel) + return -EPROBE_DEFER; + } + ptn_bridge->client = client; ptn_bridge->gpio_pd_n = of_get_named_gpio(dev->of_node, "powerdown-gpio", 0);
Force bridge connector detection at the end of the bridge attach. This is needed to detect the bridge connector early.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/bridge/ptn3460.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index 3cb3a7c..1bb4d2b 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -287,6 +287,7 @@ int ptn3460_bridge_attach(struct drm_bridge *bridge) struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); int ret;
+ ptn_bridge->connector.polled = DRM_CONNECTOR_POLL_HPD; ret = drm_connector_init(bridge->drm, &ptn_bridge->connector, &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS); if (ret) { @@ -302,6 +303,8 @@ int ptn3460_bridge_attach(struct drm_bridge *bridge) if (ptn_bridge->panel) drm_panel_attach(ptn_bridge->panel, &ptn_bridge->connector);
+ drm_helper_hpd_irq_event(ptn_bridge->connector.dev); + return ret; }
Modify driver to support gpiod interface.
Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/bridge/ptn3460.c | 88 ++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 52 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c index 1bb4d2b..6f28c13 100644 --- a/drivers/gpu/drm/bridge/ptn3460.c +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -41,8 +41,8 @@ struct ptn3460_bridge { struct drm_bridge bridge; struct edid *edid; struct drm_panel *panel; - int gpio_pd_n; - int gpio_rst_n; + struct gpio_desc *gpio_pd_n; + struct gpio_desc *gpio_rst_n; u32 edid_emulation; bool enabled; }; @@ -131,14 +131,11 @@ static void ptn3460_pre_enable(struct drm_bridge *bridge) if (ptn_bridge->enabled) return;
- if (gpio_is_valid(ptn_bridge->gpio_pd_n)) - gpio_set_value(ptn_bridge->gpio_pd_n, 1); + gpiod_set_value(ptn_bridge->gpio_pd_n, 1);
- if (gpio_is_valid(ptn_bridge->gpio_rst_n)) { - gpio_set_value(ptn_bridge->gpio_rst_n, 0); - usleep_range(10, 20); - gpio_set_value(ptn_bridge->gpio_rst_n, 1); - } + gpiod_set_value(ptn_bridge->gpio_rst_n, 0); + usleep_range(10, 20); + gpiod_set_value(ptn_bridge->gpio_rst_n, 1);
if (drm_panel_prepare(ptn_bridge->panel)) { DRM_ERROR("failed to prepare panel\n"); @@ -183,11 +180,8 @@ static void ptn3460_disable(struct drm_bridge *bridge) return; }
- if (gpio_is_valid(ptn_bridge->gpio_rst_n)) - gpio_set_value(ptn_bridge->gpio_rst_n, 1); - - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) - gpio_set_value(ptn_bridge->gpio_pd_n, 0); + gpiod_set_value(ptn_bridge->gpio_rst_n, 1); + gpiod_set_value(ptn_bridge->gpio_pd_n, 0); }
static void ptn3460_post_disable(struct drm_bridge *bridge) @@ -202,13 +196,7 @@ static void ptn3460_post_disable(struct drm_bridge *bridge)
static void ptn3460_bridge_destroy(struct drm_bridge *bridge) { - struct ptn3460_bridge *ptn_bridge = bridge_to_ptn3460(bridge); - drm_bridge_cleanup(bridge); - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) - gpio_free(ptn_bridge->gpio_pd_n); - if (gpio_is_valid(ptn_bridge->gpio_rst_n)) - gpio_free(ptn_bridge->gpio_rst_n); /* Nothing else to free, we've got devm allocated memory */ }
@@ -339,39 +327,41 @@ static int ptn3460_probe(struct i2c_client *client, }
ptn_bridge->client = client; - ptn_bridge->gpio_pd_n = of_get_named_gpio(dev->of_node, - "powerdown-gpio", 0); - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) { - ret = gpio_request_one(ptn_bridge->gpio_pd_n, - GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N"); - if (ret) { - dev_err(dev, "Request powerdown-gpio failed (%d)\n", - ret); - return ret; - } + + ptn_bridge->gpio_pd_n = devm_gpiod_get(&client->dev, "powerdown"); + if (IS_ERR(ptn_bridge->gpio_pd_n)) { + ret = PTR_ERR(ptn_bridge->gpio_pd_n); + dev_err(dev, "cannot get gpio_pd_n %d\n", ret); + return ret; }
- ptn_bridge->gpio_rst_n = of_get_named_gpio(dev->of_node, - "reset-gpio", 0); - if (gpio_is_valid(ptn_bridge->gpio_rst_n)) { - /* - * Request the reset pin low to avoid the bridge being - * initialized prematurely - */ - ret = gpio_request_one(ptn_bridge->gpio_rst_n, - GPIOF_OUT_INIT_LOW, "PTN3460_RST_N"); - if (ret) { - dev_err(dev, "Request reset-gpio failed (%d)\n", ret); - gpio_free(ptn_bridge->gpio_pd_n); - return ret; - } + ret = gpiod_direction_output(ptn_bridge->gpio_pd_n, 1); + if (ret) { + DRM_ERROR("cannot configure gpio_pd_n\n"); + return ret; + } + + ptn_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset"); + if (IS_ERR(ptn_bridge->gpio_rst_n)) { + ret = PTR_ERR(ptn_bridge->gpio_rst_n); + DRM_ERROR("cannot get gpio_rst_n %d\n", ret); + return ret; + } + /* + * Request the reset pin low to avoid the bridge being + * initialized prematurely + */ + ret = gpiod_direction_output(ptn_bridge->gpio_rst_n, 0); + if (ret) { + DRM_ERROR("cannot configure gpio_rst_n\n"); + return ret; }
ret = of_property_read_u32(dev->of_node, "edid-emulation", &ptn_bridge->edid_emulation); if (ret) { dev_err(dev, "Can't read EDID emulation value\n"); - goto err; + return ret; }
ptn_bridge->bridge.dev = dev; @@ -379,18 +369,12 @@ static int ptn3460_probe(struct i2c_client *client, ret = drm_bridge_add(&ptn_bridge->bridge); if (ret) { DRM_ERROR("Failed to add bridge\n"); - goto err; + return ret; }
i2c_set_clientdata(client, ptn_bridge);
return 0; -err: - if (gpio_is_valid(ptn_bridge->gpio_pd_n)) - gpio_free(ptn_bridge->gpio_pd_n); - if (gpio_is_valid(ptn_bridge->gpio_rst_n)) - gpio_free(ptn_bridge->gpio_rst_n); - return ret; }
static int ptn3460_remove(struct i2c_client *client)
From: Vincent Palatin vpalatin@chromium.org
This patch adds drm_bridge driver for parade DisplayPort to LVDS bridge chip.
Signed-off-by: Vincent Palatin vpalatin@chromium.org Signed-off-by: Andrew Bresticker abrestic@chromium.org Signed-off-by: Sean Paul seanpaul@chromium.org Signed-off-by: Rahul Sharma rahul.sharma@samsung.com Signed-off-by: Ajay Kumar ajaykumar.rs@samsung.com --- drivers/gpu/drm/bridge/Kconfig | 10 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ps8622.c | 681 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 692 insertions(+) create mode 100644 drivers/gpu/drm/bridge/ps8622.c
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index d35fb68..cd6061f 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -8,6 +8,16 @@ config DRM_BRIDGE menu "bridge chips" depends on DRM_BRIDGE
+config DRM_PS8622 + tristate "Parade eDP/LVDS bridge" + depends on OF + depends on I2C + select DRM_PANEL + select BACKLIGHT_LCD_SUPPORT + select BACKLIGHT_CLASS_DEVICE + help + parade eDP-LVDS bridge chip driver. + config DRM_PTN3460 tristate "PTN3460 DP/LVDS bridge" depends on OF diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index b4733e1..105da3e 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -1,3 +1,4 @@ ccflags-y := -Iinclude/drm
+obj-$(CONFIG_DRM_PS8622) += ps8622.o obj-$(CONFIG_DRM_PTN3460) += ptn3460.o diff --git a/drivers/gpu/drm/bridge/ps8622.c b/drivers/gpu/drm/bridge/ps8622.c new file mode 100644 index 0000000..bea69d1 --- /dev/null +++ b/drivers/gpu/drm/bridge/ps8622.c @@ -0,0 +1,681 @@ +/* + * Parade PS8622 eDP/LVDS bridge driver + * + * Copyright (C) 2014 Google, Inc. + * + * 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 <linux/backlight.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_panel.h> + +#include "drmP.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +/* Brightness scale on the Parade chip */ +#define PS8622_MAX_BRIGHTNESS 0xff + +/* Timings taken from the version 1.7 datasheet for the PS8622/PS8625 */ +#define PS8622_POWER_RISE_T1_MIN_US 10 +#define PS8622_POWER_RISE_T1_MAX_US 10000 +#define PS8622_RST_HIGH_T2_MIN_US 3000 +#define PS8622_RST_HIGH_T2_MAX_US 30000 +#define PS8622_PWMO_END_T12_MS 200 +#define PS8622_POWER_FALL_T16_MAX_US 10000 +#define PS8622_POWER_OFF_T17_MS 500 + +#if ((PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US) > \ + (PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US)) +#error "T2.min + T1.max must be less than T2.max + T1.min" +#endif + +struct ps8622_bridge { + struct drm_connector connector; + struct i2c_client *client; + struct drm_bridge bridge; + struct drm_panel *panel; + struct regulator *v12; + struct backlight_device *bl; + + struct gpio_desc *gpio_slp; + struct gpio_desc *gpio_rst; + + u32 max_lane_count; + u32 lane_count; + + bool enabled; +}; + +static inline struct ps8622_bridge * + bridge_to_ps8622(struct drm_bridge *bridge) +{ + return container_of(bridge, struct ps8622_bridge, bridge); +} + +static inline struct ps8622_bridge * + connector_to_ps8622(struct drm_connector *connector) +{ + return container_of(connector, struct ps8622_bridge, connector); +} + +static int ps8622_set(struct i2c_client *client, u8 page, u8 reg, u8 val) +{ + int ret; + struct i2c_adapter *adap = client->adapter; + struct i2c_msg msg; + u8 data[] = {reg, val}; + + msg.addr = client->addr + page; + msg.flags = 0; + msg.len = sizeof(data); + msg.buf = data; + + ret = i2c_transfer(adap, &msg, 1); + if (ret != 1) + pr_warn("PS8622 I2C write (0x%02x,0x%02x,0x%02x) failed: %d\n", + client->addr + page, reg, val, ret); + return !(ret == 1); +} + +static int ps8622_send_config(struct ps8622_bridge *ps8622) +{ + struct i2c_client *cl = ps8622->client; + int err = 0; + + /* HPD low */ + err = ps8622_set(cl, 0x02, 0xa1, 0x01); + if (err) + goto error; + + /* SW setting: [1:0] SW output 1.2V voltage is lower to 96% */ + err = ps8622_set(cl, 0x04, 0x14, 0x01); + if (err) + goto error; + + /* RCO SS setting: [5:4] = b01 0.5%, b10 1%, b11 1.5% */ + err = ps8622_set(cl, 0x04, 0xe3, 0x20); + if (err) + goto error; + + /* [7] RCO SS enable */ + err = ps8622_set(cl, 0x04, 0xe2, 0x80); + if (err) + goto error; + + /* RPHY Setting + * [3:2] CDR tune wait cycle before measure for fine tune + * b00: 1us b01: 0.5us b10:2us, b11: 4us + */ + err = ps8622_set(cl, 0x04, 0x8a, 0x0c); + if (err) + goto error; + + /* [3] RFD always on */ + err = ps8622_set(cl, 0x04, 0x89, 0x08); + if (err) + goto error; + + /* CTN lock in/out: 20000ppm/80000ppm. Lock out 2 times. */ + err = ps8622_set(cl, 0x04, 0x71, 0x2d); + if (err) + goto error; + + /* 2.7G CDR settings: NOF=40LSB for HBR CDR setting */ + err = ps8622_set(cl, 0x04, 0x7d, 0x07); + if (err) + goto error; + + /* [1:0] Fmin=+4bands */ + err = ps8622_set(cl, 0x04, 0x7b, 0x00); + if (err) + goto error; + + /* [7:5] DCO_FTRNG=+-40% */ + err = ps8622_set(cl, 0x04, 0x7a, 0xfd); + if (err) + goto error; + + /* 1.62G CDR settings: [5:2]NOF=64LSB [1:0]DCO scale is 2/5 */ + err = ps8622_set(cl, 0x04, 0xc0, 0x12); + if (err) + goto error; + + /* Gitune=-37% */ + err = ps8622_set(cl, 0x04, 0xc1, 0x92); + if (err) + goto error; + + /* Fbstep=100% */ + err = ps8622_set(cl, 0x04, 0xc2, 0x1c); + if (err) + goto error; + + /* [7] LOS signal disable */ + err = ps8622_set(cl, 0x04, 0x32, 0x80); + if (err) + goto error; + + /* RPIO Setting: [7:4] LVDS driver bias current : 75% (250mV swing) */ + err = ps8622_set(cl, 0x04, 0x00, 0xb0); + if (err) + goto error; + + /* [7:6] Right-bar GPIO output strength is 8mA */ + err = ps8622_set(cl, 0x04, 0x15, 0x40); + if (err) + goto error; + + /* EQ Training State Machine Setting, RCO calibration start */ + err = ps8622_set(cl, 0x04, 0x54, 0x10); + if (err) + goto error; + + /* Logic, needs more than 10 I2C command */ + /* [4:0] MAX_LANE_COUNT set to max supported lanes */ + err = ps8622_set(cl, 0x01, 0x02, 0x80 | ps8622->max_lane_count); + if (err) + goto error; + + /* [4:0] LANE_COUNT_SET set to chosen lane count */ + err = ps8622_set(cl, 0x01, 0x21, 0x80 | ps8622->lane_count); + if (err) + goto error; + + err = ps8622_set(cl, 0x00, 0x52, 0x20); + if (err) + goto error; + + /* HPD CP toggle enable */ + err = ps8622_set(cl, 0x00, 0xf1, 0x03); + if (err) + goto error; + + err = ps8622_set(cl, 0x00, 0x62, 0x41); + if (err) + goto error; + + /* Counter number, add 1ms counter delay */ + err = ps8622_set(cl, 0x00, 0xf6, 0x01); + if (err) + goto error; + + /* [6]PWM function control by DPCD0040f[7], default is PWM block */ + err = ps8622_set(cl, 0x00, 0x77, 0x06); + if (err) + goto error; + + /* 04h Adjust VTotal toleranceto fix the 30Hz no display issue */ + err = ps8622_set(cl, 0x00, 0x4c, 0x04); + if (err) + goto error; + + /* DPCD00400='h00, Parade OUI ='h001cf8 */ + err = ps8622_set(cl, 0x01, 0xc0, 0x00); + if (err) + goto error; + + /* DPCD00401='h1c */ + err = ps8622_set(cl, 0x01, 0xc1, 0x1c); + if (err) + goto error; + + /* DPCD00402='hf8 */ + err = ps8622_set(cl, 0x01, 0xc2, 0xf8); + if (err) + goto error; + + /* DPCD403~408 = ASCII code, D2SLV5='h4432534c5635 */ + err = ps8622_set(cl, 0x01, 0xc3, 0x44); + if (err) + goto error; + + /* DPCD404 */ + err = ps8622_set(cl, 0x01, 0xc4, 0x32); + if (err) + goto error; + + /* DPCD405 */ + err = ps8622_set(cl, 0x01, 0xc5, 0x53); + if (err) + goto error; + + /* DPCD406 */ + err = ps8622_set(cl, 0x01, 0xc6, 0x4c); + if (err) + goto error; + + /* DPCD407 */ + err = ps8622_set(cl, 0x01, 0xc7, 0x56); + if (err) + goto error; + + /* DPCD408 */ + err = ps8622_set(cl, 0x01, 0xc8, 0x35); + if (err) + goto error; + + /* DPCD40A, Initial Code major revision '01' */ + err = ps8622_set(cl, 0x01, 0xca, 0x01); + if (err) + goto error; + + /* DPCD40B, Initial Code minor revision '05' */ + err = ps8622_set(cl, 0x01, 0xcb, 0x05); + if (err) + goto error; + + + if (ps8622->bl) { + /* DPCD720, internal PWM */ + err = ps8622_set(cl, 0x01, 0xa5, 0xa0); + if (err) + goto error; + + /* FFh for 100% brightness, 0h for 0% brightness */ + err = ps8622_set(cl, 0x01, 0xa7, + ps8622->bl->props.brightness); + if (err) + goto error; + } else { + /* DPCD720, external PWM */ + err = ps8622_set(cl, 0x01, 0xa5, 0x80); + if (err) + goto error; + } + + /* Set LVDS output as 6bit-VESA mapping, single LVDS channel */ + err = ps8622_set(cl, 0x01, 0xcc, 0x13); + if (err) + goto error; + + /* Enable SSC set by register */ + err = ps8622_set(cl, 0x02, 0xb1, 0x20); + if (err) + goto error; + + /* Set SSC enabled and +/-1% central spreading */ + err = ps8622_set(cl, 0x04, 0x10, 0x16); + if (err) + goto error; + + /* Logic end */ + /* MPU Clock source: LC => RCO */ + err = ps8622_set(cl, 0x04, 0x59, 0x60); + if (err) + goto error; + + /* LC -> RCO */ + err = ps8622_set(cl, 0x04, 0x54, 0x14); + if (err) + goto error; + + /* HPD high */ + err = ps8622_set(cl, 0x02, 0xa1, 0x91); + +error: + return err ? -EIO : 0; +} + +static int ps8622_backlight_update(struct backlight_device *bl) +{ + struct ps8622_bridge *ps8622 = dev_get_drvdata(&bl->dev); + int ret, brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness = 0; + + if (!ps8622->enabled) + return -EINVAL; + + ret = ps8622_set(ps8622->client, 0x01, 0xa7, brightness); + + return ret; +} + +static const struct backlight_ops ps8622_backlight_ops = { + .update_status = ps8622_backlight_update, +}; + +static void ps8622_pre_enable(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); + int ret; + + if (ps8622->enabled) + return; + + gpiod_set_value(ps8622->gpio_rst, 0); + + if (ps8622->v12) { + ret = regulator_enable(ps8622->v12); + if (ret) + DRM_ERROR("fails to enable ps8622->v12"); + } + + if (drm_panel_prepare(ps8622->panel)) { + DRM_ERROR("failed to prepare panel\n"); + return; + } + + gpiod_set_value(ps8622->gpio_slp, 1); + + /* + * T1 is the range of time that it takes for the power to rise after we + * enable the lcd/ps8622 fet. T2 is the range of time in which the + * data sheet specifies we should deassert the reset pin. + * + * If it takes T1.max for the power to rise, we need to wait atleast + * T2.min before deasserting the reset pin. If it takes T1.min for the + * power to rise, we need to wait at most T2.max before deasserting the + * reset pin. + */ + usleep_range(PS8622_RST_HIGH_T2_MIN_US + PS8622_POWER_RISE_T1_MAX_US, + PS8622_RST_HIGH_T2_MAX_US + PS8622_POWER_RISE_T1_MIN_US); + + gpiod_set_value(ps8622->gpio_rst, 1); + + /* wait 20ms after RST high */ + usleep_range(20000, 30000); + + ret = ps8622_send_config(ps8622); + if (ret) { + DRM_ERROR("Failed to send config to bridge (%d)\n", ret); + return; + } + + ps8622->enabled = true; +} + +static void ps8622_enable(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); + + if (drm_panel_enable(ps8622->panel)) { + DRM_ERROR("failed to enable panel\n"); + return; + } +} + +static void ps8622_disable(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); + + if (drm_panel_disable(ps8622->panel)) { + DRM_ERROR("failed to disable panel\n"); + return; + } + msleep(PS8622_PWMO_END_T12_MS); +} + +static void ps8622_post_disable(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); + + if (!ps8622->enabled) + return; + + ps8622->enabled = false; + + /* + * This doesn't matter if the regulators are turned off, but something + * else might keep them on. In that case, we want to assert the slp gpio + * to lower power. + */ + gpiod_set_value(ps8622->gpio_slp, 0); + + if (drm_panel_unprepare(ps8622->panel)) { + DRM_ERROR("failed to unprepare panel\n"); + return; + } + + if (ps8622->v12) + regulator_disable(ps8622->v12); + + /* + * Sleep for at least the amount of time that it takes the power rail to + * fall to prevent asserting the rst gpio from doing anything. + */ + usleep_range(PS8622_POWER_FALL_T16_MAX_US, + 2 * PS8622_POWER_FALL_T16_MAX_US); + gpiod_set_value(ps8622->gpio_rst, 0); + + msleep(PS8622_POWER_OFF_T17_MS); +} + +static void ps8622_destroy(struct drm_bridge *bridge) +{ + drm_bridge_cleanup(bridge); +} + +static int ps8622_get_modes(struct drm_connector *connector) +{ + struct ps8622_bridge *ps8622; + + ps8622 = connector_to_ps8622(connector); + + return drm_panel_get_modes(ps8622->panel); +} + +static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector) +{ + struct ps8622_bridge *ps8622; + + ps8622 = connector_to_ps8622(connector); + + return ps8622->bridge.encoder; +} + +static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = { + .get_modes = ps8622_get_modes, + .best_encoder = ps8622_best_encoder, +}; + +static enum drm_connector_status ps8622_detect(struct drm_connector *connector, + bool force) +{ + return connector_status_connected; +} + +static void ps8622_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs ps8622_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = ps8622_detect, + .destroy = ps8622_connector_destroy, +}; + +int ps8622_attach(struct drm_bridge *bridge) +{ + struct ps8622_bridge *ps8622 = bridge_to_ps8622(bridge); + int ret; + + ps8622->connector.polled = DRM_CONNECTOR_POLL_HPD; + ret = drm_connector_init(bridge->drm, &ps8622->connector, + &ps8622_connector_funcs, DRM_MODE_CONNECTOR_LVDS); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + return ret; + } + drm_connector_helper_add(&ps8622->connector, + &ps8622_connector_helper_funcs); + drm_connector_register(&ps8622->connector); + drm_mode_connector_attach_encoder(&ps8622->connector, + bridge->encoder); + + if (ps8622->panel) + drm_panel_attach(ps8622->panel, &ps8622->connector); + + drm_helper_hpd_irq_event(ps8622->connector.dev); + + return ret; +} + +static const struct drm_bridge_funcs ps8622_bridge_funcs = { + .pre_enable = ps8622_pre_enable, + .enable = ps8622_enable, + .disable = ps8622_disable, + .post_disable = ps8622_post_disable, + .destroy = ps8622_destroy, + .attach = ps8622_attach, +}; + +static const struct of_device_id ps8622_devices[] = { + {.compatible = "parade,ps8622",}, + {.compatible = "parade,ps8625",}, + {} +}; +MODULE_DEVICE_TABLE(of, ps8622_devices); + +static int ps8622_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *panel_node; + struct ps8622_bridge *ps8622; + int ret; + + ps8622 = devm_kzalloc(dev, sizeof(*ps8622), GFP_KERNEL); + if (!ps8622) + return -ENOMEM; + + panel_node = of_parse_phandle(dev->of_node, "panel", 0); + if (panel_node) { + ps8622->panel = of_drm_find_panel(panel_node); + of_node_put(panel_node); + if (!ps8622->panel) + return -EPROBE_DEFER; + } + + ps8622->client = client; + + ps8622->v12 = devm_regulator_get(dev, "vdd12"); + if (IS_ERR(ps8622->v12)) { + dev_info(dev, "no 1.2v regulator found for PS8622\n"); + ps8622->v12 = NULL; + } + + ps8622->gpio_slp = devm_gpiod_get(dev, "sleep"); + if (IS_ERR(ps8622->gpio_slp)) { + ret = PTR_ERR(ps8622->gpio_slp); + dev_err(dev, "cannot get gpio_slp %d\n", ret); + return ret; + } + ret = gpiod_direction_output(ps8622->gpio_slp, 1); + if (ret) { + dev_err(dev, "cannot configure gpio_slp\n"); + return ret; + } + + ps8622->gpio_rst = devm_gpiod_get(dev, "reset"); + if (IS_ERR(ps8622->gpio_rst)) { + ret = PTR_ERR(ps8622->gpio_rst); + dev_err(dev, "cannot get gpio_rst %d\n", ret); + return ret; + } + /* + * Assert the reset pin high to avoid the bridge being + * initialized prematurely + */ + ret = gpiod_direction_output(ps8622->gpio_rst, 1); + if (ret) { + dev_err(dev, "cannot configure gpio_rst\n"); + return ret; + } + + ps8622->max_lane_count = id->driver_data; + + if (of_property_read_u32(dev->of_node, "lane-count", + &ps8622->lane_count)) { + ps8622->lane_count = ps8622->max_lane_count; + } else if (ps8622->lane_count > ps8622->max_lane_count) { + dev_info(dev, "lane-count property is too high," + "using max_lane_count\n"); + ps8622->lane_count = ps8622->max_lane_count; + } + + if (!of_find_property(dev->of_node, "use-external-pwm", NULL)) { + ps8622->bl = backlight_device_register("ps8622-backlight", + dev, ps8622, &ps8622_backlight_ops, + NULL); + if (IS_ERR(ps8622->bl)) { + DRM_ERROR("failed to register backlight\n"); + ret = PTR_ERR(ps8622->bl); + ps8622->bl = NULL; + return ret; + } + ps8622->bl->props.max_brightness = PS8622_MAX_BRIGHTNESS; + ps8622->bl->props.brightness = PS8622_MAX_BRIGHTNESS; + } + + ps8622->bridge.dev = dev; + ps8622->bridge.funcs = &ps8622_bridge_funcs; + ret = drm_bridge_add(&ps8622->bridge); + if (ret) { + DRM_ERROR("Failed to add bridge\n"); + return ret; + } + + i2c_set_clientdata(client, ps8622); + + return 0; +} + +static int ps8622_remove(struct i2c_client *client) +{ + struct ps8622_bridge *ps8622 = i2c_get_clientdata(client); + + if (ps8622->bl) + backlight_device_unregister(ps8622->bl); + + drm_bridge_remove(&ps8622->bridge); + + return 0; +} + +static const struct i2c_device_id ps8622_i2c_table[] = { + /* Device type, max_lane_count */ + {"ps8622", 1}, + {"ps8625", 2}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table); + +struct i2c_driver ps8622_driver = { + .id_table = ps8622_i2c_table, + .probe = ps8622_probe, + .remove = ps8622_remove, + .driver = { + .name = "ps8622", + .owner = THIS_MODULE, + .of_match_table = ps8622_devices, + }, +}; +module_i2c_driver(ps8622_driver); + +MODULE_AUTHOR("Vincent Palatin vpalatin@chromium.org"); +MODULE_DESCRIPTION("Parade ps8622/ps8625 eDP-LVDS converter driver"); +MODULE_LICENSE("GPL v2");
ping.
On Wed, Aug 27, 2014 at 7:59 PM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
This series is based on master branch of Linus tree at: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
I have tested this after adding few DT changes for exynos5250-snow and exynos5420-peach-pit boards.
The V4 series of this particular patchset was also tested by: Rahul Sharma rahul.sharma@samsung.com Javier Martinez Canillas javier@dowhile0.org
Changes since V2: -- Address comments from Jingoo Han for ps8622 driver -- Address comments from Daniel, Rob and Thierry regarding bridge chaining -- Address comments from Thierry regarding the names for new drm_panel functions
Changes since V3: -- Remove hotplug based initialization of exynos_dp -- Make exynos_dp work directly with drm_panel, remove dependency on panel_binder -- Minor cleanups in panel_binder and panel_lvds driver
Changes since V4: -- Use gpiod interface for panel-lvds and ps8622 drivers. -- Address comments from Javier. -- Fix compilation issues when PANEL_BINDER is selected as module. -- Split Documentation patches from driver patches. -- Rebase on top of the tree.
Changes since V5: -- Modify bridge drivers to support driver model. -- Drop the concept of bridge chain(sincle there are no 2 real bridges) Hence drop bridge-panel_binder layer. -- Drop panel-lvds driver and accomodate the required changes in panel-simple driver. -- Use gpiod interface in ptn3460 driver. -- Address all comments by Thierry Reding for V5 series. -- Address comments from Sean Paul for exynos_dp_commit issue.
Changes since V6: -- Panel patches were seperated and they are merged already. -- Fix few issues with ptn3460, before modifying the bridge core. -- Modify drm_bridge as per Thierry's comments for V6 series. -- Add drm_bridge changes minimally without breaking existing code. -- Add new features for ptn3460, step-by-step. -- Address comments from Thierry and Andreas for ptn3460 and ps8622. -- Split documentation patches from driver patches.
"[PATCH V7 5/12] drm/exynos: dp: support drm_bridge" introduces following Kconfig error: drivers/video/fbdev/Kconfig:5:error: recursive dependency detected! drivers/video/fbdev/Kconfig:5: symbol FB is selected by DRM_KMS_FB_HELPER drivers/gpu/drm/Kconfig:39: symbol DRM_KMS_FB_HELPER depends on DRM_KMS_HELPER drivers/gpu/drm/Kconfig:33: symbol DRM_KMS_HELPER is selected by DRM_BRIDGE drivers/gpu/drm/bridge/Kconfig:1: symbol DRM_BRIDGE is selected by DRM_EXYNOS_DP drivers/gpu/drm/exynos/Kconfig:53: symbol DRM_EXYNOS_DP depends on DRM_EXYNOS_FIMD drivers/gpu/drm/exynos/Kconfig:28: symbol DRM_EXYNOS_FIMD depends on FB_S3C drivers/video/fbdev/Kconfig:2038: symbol FB_S3C depends on FB
What's the best way to fix the above ambiguity?
Ajay Kumar (11): [PATCH V7 1/12] drm/bridge: ptn3460: Few trivial cleanups [PATCH V7 2/12] drm/bridge: do not pass drm_bridge_funcs to drm_bridge_init [PATCH V7 3/12] drm/bridge: Add helper functions for drm_bridge [PATCH V7 4/12] drm/bridge: ptn3460: convert to i2c driver model [PATCH V7 5/12] drm/exynos: dp: support drm_bridge [PATCH V7 6/12] drm/bridge: ptn3460: support drm_panel [PATCH V7 7/12] drm/bridge: ptn3460: probe connector at the end of bridge attach [PATCH V7 8/12] drm/bridge: ptn3460: use gpiod interface [PATCH V7 9/12] Documentation: drm: bridge: move to video/bridge [PATCH V7 10/12] Documentation: devicetree: Add vendor prefix for parade [PATCH V7 11/12] Documentation: bridge: Add documentation for ps8622 DT properties
Vincent Palatin (1): [PATCH V7 12/12] drm/bridge: Add i2c based driver for ps8622/ps8625 bridge
.../devicetree/bindings/drm/bridge/ptn3460.txt | 27 - .../devicetree/bindings/vendor-prefixes.txt | 1 + .../devicetree/bindings/video/bridge/ps8622.txt | 20 + .../devicetree/bindings/video/bridge/ptn3460.txt | 27 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/bridge/Kconfig | 30 +- drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/ps8622.c | 681 ++++++++++++++++++++ drivers/gpu/drm/bridge/ptn3460.c | 302 +++++---- drivers/gpu/drm/drm_bridge.c | 102 +++ drivers/gpu/drm/drm_crtc.c | 9 +- drivers/gpu/drm/exynos/Kconfig | 3 +- drivers/gpu/drm/exynos/exynos_dp_core.c | 54 +- drivers/gpu/drm/exynos/exynos_dp_core.h | 1 + drivers/gpu/drm/msm/hdmi/hdmi_bridge.c | 7 +- drivers/gpu/drm/sti/sti_hda.c | 3 +- drivers/gpu/drm/sti/sti_hdmi.c | 3 +- include/drm/drm_crtc.h | 15 +- 18 files changed, 1098 insertions(+), 189 deletions(-) delete mode 100644 Documentation/devicetree/bindings/drm/bridge/ptn3460.txt create mode 100644 Documentation/devicetree/bindings/video/bridge/ps8622.txt create mode 100644 Documentation/devicetree/bindings/video/bridge/ptn3460.txt create mode 100644 drivers/gpu/drm/bridge/ps8622.c create mode 100644 drivers/gpu/drm/drm_bridge.c
-- 1.7.9.5
[adding Laurent Pinchart to cc who had concerns with a previous version of this patch-set]
Hello Ajay,
On Wed, Aug 27, 2014 at 4:29 PM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
This series is based on master branch of Linus tree at: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
I have tested this after adding few DT changes for exynos5250-snow and exynos5420-peach-pit boards.
The V4 series of this particular patchset was also tested by: Rahul Sharma rahul.sharma@samsung.com Javier Martinez Canillas javier@dowhile0.org
Changes since V2: -- Address comments from Jingoo Han for ps8622 driver -- Address comments from Daniel, Rob and Thierry regarding bridge chaining -- Address comments from Thierry regarding the names for new drm_panel functions
Changes since V3: -- Remove hotplug based initialization of exynos_dp -- Make exynos_dp work directly with drm_panel, remove dependency on panel_binder -- Minor cleanups in panel_binder and panel_lvds driver
Changes since V4: -- Use gpiod interface for panel-lvds and ps8622 drivers. -- Address comments from Javier. -- Fix compilation issues when PANEL_BINDER is selected as module. -- Split Documentation patches from driver patches. -- Rebase on top of the tree.
Changes since V5: -- Modify bridge drivers to support driver model. -- Drop the concept of bridge chain(sincle there are no 2 real bridges) Hence drop bridge-panel_binder layer. -- Drop panel-lvds driver and accomodate the required changes in panel-simple driver. -- Use gpiod interface in ptn3460 driver. -- Address all comments by Thierry Reding for V5 series. -- Address comments from Sean Paul for exynos_dp_commit issue.
Changes since V6: -- Panel patches were seperated and they are merged already. -- Fix few issues with ptn3460, before modifying the bridge core. -- Modify drm_bridge as per Thierry's comments for V6 series. -- Add drm_bridge changes minimally without breaking existing code. -- Add new features for ptn3460, step-by-step. -- Address comments from Thierry and Andreas for ptn3460 and ps8622. -- Split documentation patches from driver patches.
I've tested your series on an Exynos5420 Peach Pit and an Exynos5250 Snow Chromebooks and display worked for me on both machines.
I also needed "[PATCH] drm/panel: simple: Add AUO B116XW03 panel support" [0] which does not apply cleanly on linux-next so you may want to do a re-spin for that patch.
For Snow I also had to disable CONFIG_FB_SIMPLE, otherwise I just saw a blink on boot and only the backlight remained turned on (no display output). I don't know if that is expected since IIUC it should be possible to do a transition from simplefb to a DRM/KMS driver. I don't have a serial console hooked on this machine so I couldn't debug it further, sorry.
Also, I saw that Laurent mentioned some concerns today about the previous version (v6) of your series [1]. I guess he missed this v7 although AFAIU there was no fundamental change on this one so his concerns remains? I was really hoping that this could make it to 3.18 since display support is one of the last major functionalities that is missing on these machines.
Best regards, Javier
[0]: http://patchwork.ozlabs.org/patch/384744/ [1]: http://www.spinics.net/lists/linux-samsung-soc/msg36791.html
Hi Javier,
On Tuesday 16 September 2014 14:44:02 Javier Martinez Canillas wrote:
[adding Laurent Pinchart to cc who had concerns with a previous version of this patch-set]
Thank you, I've indeed missed v7.
On Wed, Aug 27, 2014 at 4:29 PM, Ajay Kumar wrote:
This series is based on master branch of Linus tree at: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
I have tested this after adding few DT changes for exynos5250-snow and exynos5420-peach-pit boards.
The V4 series of this particular patchset was also tested by: Rahul Sharma rahul.sharma@samsung.com Javier Martinez Canillas javier@dowhile0.org
Changes since V2: -- Address comments from Jingoo Han for ps8622 driver -- Address comments from Daniel, Rob and Thierry regarding bridge chaining -- Address comments from Thierry regarding the names for new drm_panel functions
Changes since V3: -- Remove hotplug based initialization of exynos_dp -- Make exynos_dp work directly with drm_panel, remove dependency on panel_binder -- Minor cleanups in panel_binder and panel_lvds driver
Changes since V4: -- Use gpiod interface for panel-lvds and ps8622 drivers. -- Address comments from Javier. -- Fix compilation issues when PANEL_BINDER is selected as module. -- Split Documentation patches from driver patches. -- Rebase on top of the tree.
Changes since V5: -- Modify bridge drivers to support driver model. -- Drop the concept of bridge chain(sincle there are no 2 real bridges) Hence drop bridge-panel_binder layer. -- Drop panel-lvds driver and accomodate the required changes in panel-simple driver. -- Use gpiod interface in ptn3460 driver. -- Address all comments by Thierry Reding for V5 series. -- Address comments from Sean Paul for exynos_dp_commit issue.
Changes since V6: -- Panel patches were seperated and they are merged already. -- Fix few issues with ptn3460, before modifying the bridge core. -- Modify drm_bridge as per Thierry's comments for V6 series. -- Add drm_bridge changes minimally without breaking existing code. -- Add new features for ptn3460, step-by-step. -- Address comments from Thierry and Andreas for ptn3460 and ps8622. -- Split documentation patches from driver patches.
I've tested your series on an Exynos5420 Peach Pit and an Exynos5250 Snow Chromebooks and display worked for me on both machines.
I also needed "[PATCH] drm/panel: simple: Add AUO B116XW03 panel support" [0] which does not apply cleanly on linux-next so you may want to do a re-spin for that patch.
For Snow I also had to disable CONFIG_FB_SIMPLE, otherwise I just saw a blink on boot and only the backlight remained turned on (no display output). I don't know if that is expected since IIUC it should be possible to do a transition from simplefb to a DRM/KMS driver. I don't have a serial console hooked on this machine so I couldn't debug it further, sorry.
Also, I saw that Laurent mentioned some concerns today about the previous version (v6) of your series [1]. I guess he missed this v7 although AFAIU there was no fundamental change on this one so his concerns remains? I was really hoping that this could make it to 3.18 since display support is one of the last major functionalities that is missing on these machines.
My main concern as far as merging this patch set goes is why we can't use the component framework instead of introducing yet another new subsystem-specific component registration framework. My other concerns could be addressed as follow-up patches (but I'd like to start discussing them now).
On Tue, Sep 16, 2014 at 6:14 PM, Javier Martinez Canillas javier@dowhile0.org wrote:
[adding Laurent Pinchart to cc who had concerns with a previous version of this patch-set]
Hello Ajay,
On Wed, Aug 27, 2014 at 4:29 PM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
This series is based on master branch of Linus tree at: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
I have tested this after adding few DT changes for exynos5250-snow and exynos5420-peach-pit boards.
The V4 series of this particular patchset was also tested by: Rahul Sharma rahul.sharma@samsung.com Javier Martinez Canillas javier@dowhile0.org
Changes since V2: -- Address comments from Jingoo Han for ps8622 driver -- Address comments from Daniel, Rob and Thierry regarding bridge chaining -- Address comments from Thierry regarding the names for new drm_panel functions
Changes since V3: -- Remove hotplug based initialization of exynos_dp -- Make exynos_dp work directly with drm_panel, remove dependency on panel_binder -- Minor cleanups in panel_binder and panel_lvds driver
Changes since V4: -- Use gpiod interface for panel-lvds and ps8622 drivers. -- Address comments from Javier. -- Fix compilation issues when PANEL_BINDER is selected as module. -- Split Documentation patches from driver patches. -- Rebase on top of the tree.
Changes since V5: -- Modify bridge drivers to support driver model. -- Drop the concept of bridge chain(sincle there are no 2 real bridges) Hence drop bridge-panel_binder layer. -- Drop panel-lvds driver and accomodate the required changes in panel-simple driver. -- Use gpiod interface in ptn3460 driver. -- Address all comments by Thierry Reding for V5 series. -- Address comments from Sean Paul for exynos_dp_commit issue.
Changes since V6: -- Panel patches were seperated and they are merged already. -- Fix few issues with ptn3460, before modifying the bridge core. -- Modify drm_bridge as per Thierry's comments for V6 series. -- Add drm_bridge changes minimally without breaking existing code. -- Add new features for ptn3460, step-by-step. -- Address comments from Thierry and Andreas for ptn3460 and ps8622. -- Split documentation patches from driver patches.
I've tested your series on an Exynos5420 Peach Pit and an Exynos5250 Snow Chromebooks and display worked for me on both machines.
Great!
I also needed "[PATCH] drm/panel: simple: Add AUO B116XW03 panel support" [0] which does not apply cleanly on linux-next so you may want to do a re-spin for that patch.
Ok. I will take care of this in next version.
For Snow I also had to disable CONFIG_FB_SIMPLE, otherwise I just saw a blink on boot and only the backlight remained turned on (no display output). I don't know if that is expected since IIUC it should be possible to do a transition from simplefb to a DRM/KMS driver. I don't have a serial console hooked on this machine so I couldn't debug it further, sorry.
I am just wondering how SIMPLE FB can affect DRM based display. I am not even sure if both can co-exist or not. Is there anything we can do with bootargs instead of CONFIG?
Ajay
Also, I saw that Laurent mentioned some concerns today about the previous version (v6) of your series [1]. I guess he missed this v7 although AFAIU there was no fundamental change on this one so his concerns remains? I was really hoping that this could make it to 3.18 since display support is one of the last major functionalities that is missing on these machines.
Best regards, Javier
Hi,
On Wed, Sep 17, 2014 at 3:02 PM, Ajay kumar ajaynumb@gmail.com wrote:
On Tue, Sep 16, 2014 at 6:14 PM, Javier Martinez Canillas javier@dowhile0.org wrote:
[adding Laurent Pinchart to cc who had concerns with a previous version of this patch-set]
Hello Ajay,
On Wed, Aug 27, 2014 at 4:29 PM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
This series is based on master branch of Linus tree at: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
I have tested this after adding few DT changes for exynos5250-snow and exynos5420-peach-pit boards.
The V4 series of this particular patchset was also tested by: Rahul Sharma rahul.sharma@samsung.com Javier Martinez Canillas javier@dowhile0.org
Changes since V2: -- Address comments from Jingoo Han for ps8622 driver -- Address comments from Daniel, Rob and Thierry regarding bridge chaining -- Address comments from Thierry regarding the names for new drm_panel functions
Changes since V3: -- Remove hotplug based initialization of exynos_dp -- Make exynos_dp work directly with drm_panel, remove dependency on panel_binder -- Minor cleanups in panel_binder and panel_lvds driver
Changes since V4: -- Use gpiod interface for panel-lvds and ps8622 drivers. -- Address comments from Javier. -- Fix compilation issues when PANEL_BINDER is selected as module. -- Split Documentation patches from driver patches. -- Rebase on top of the tree.
Changes since V5: -- Modify bridge drivers to support driver model. -- Drop the concept of bridge chain(sincle there are no 2 real bridges) Hence drop bridge-panel_binder layer. -- Drop panel-lvds driver and accomodate the required changes in panel-simple driver. -- Use gpiod interface in ptn3460 driver. -- Address all comments by Thierry Reding for V5 series. -- Address comments from Sean Paul for exynos_dp_commit issue.
Changes since V6: -- Panel patches were seperated and they are merged already. -- Fix few issues with ptn3460, before modifying the bridge core. -- Modify drm_bridge as per Thierry's comments for V6 series. -- Add drm_bridge changes minimally without breaking existing code. -- Add new features for ptn3460, step-by-step. -- Address comments from Thierry and Andreas for ptn3460 and ps8622. -- Split documentation patches from driver patches.
I've tested your series on an Exynos5420 Peach Pit and an Exynos5250 Snow Chromebooks and display worked for me on both machines.
Great!
I also needed "[PATCH] drm/panel: simple: Add AUO B116XW03 panel support" [0] which does not apply cleanly on linux-next so you may want to do a re-spin for that patch.
Ok. I will take care of this in next version.
For Snow I also had to disable CONFIG_FB_SIMPLE, otherwise I just saw a blink on boot and only the backlight remained turned on (no display output). I don't know if that is expected since IIUC it should be possible to do a transition from simplefb to a DRM/KMS driver. I don't have a serial console hooked on this machine so I couldn't debug it further, sorry.
I am just wondering how SIMPLE FB can affect DRM based display. I am not even sure if both can co-exist or not. Is there anything we can do with bootargs instead of CONFIG?
Ajay
Also, I saw that Laurent mentioned some concerns today about the previous version (v6) of your series [1]. I guess he missed this v7 although AFAIU there was no fundamental change on this one so his concerns remains? I was really hoping that this could make it to 3.18 since display support is one of the last major functionalities that is missing on these machines.
Best regards, Javier
I am thinking of respinning this patchset based on Tomi's suggestion for using video ports instead of phandles. So, please go through this patchset once again and let me know if any other review comment need to be addressed! Day by day, no of boards using bridge is increasing and its a pain to consolidate all of them when I am respinning :(
Ajay
On Wed, Sep 17, 2014 at 03:02:48PM +0530, Ajay kumar wrote:
On Tue, Sep 16, 2014 at 6:14 PM, Javier Martinez Canillas javier@dowhile0.org wrote:
[adding Laurent Pinchart to cc who had concerns with a previous version of this patch-set]
Hello Ajay,
On Wed, Aug 27, 2014 at 4:29 PM, Ajay Kumar ajaykumar.rs@samsung.com wrote:
This series is based on master branch of Linus tree at: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
I have tested this after adding few DT changes for exynos5250-snow and exynos5420-peach-pit boards.
The V4 series of this particular patchset was also tested by: Rahul Sharma rahul.sharma@samsung.com Javier Martinez Canillas javier@dowhile0.org
Changes since V2: -- Address comments from Jingoo Han for ps8622 driver -- Address comments from Daniel, Rob and Thierry regarding bridge chaining -- Address comments from Thierry regarding the names for new drm_panel functions
Changes since V3: -- Remove hotplug based initialization of exynos_dp -- Make exynos_dp work directly with drm_panel, remove dependency on panel_binder -- Minor cleanups in panel_binder and panel_lvds driver
Changes since V4: -- Use gpiod interface for panel-lvds and ps8622 drivers. -- Address comments from Javier. -- Fix compilation issues when PANEL_BINDER is selected as module. -- Split Documentation patches from driver patches. -- Rebase on top of the tree.
Changes since V5: -- Modify bridge drivers to support driver model. -- Drop the concept of bridge chain(sincle there are no 2 real bridges) Hence drop bridge-panel_binder layer. -- Drop panel-lvds driver and accomodate the required changes in panel-simple driver. -- Use gpiod interface in ptn3460 driver. -- Address all comments by Thierry Reding for V5 series. -- Address comments from Sean Paul for exynos_dp_commit issue.
Changes since V6: -- Panel patches were seperated and they are merged already. -- Fix few issues with ptn3460, before modifying the bridge core. -- Modify drm_bridge as per Thierry's comments for V6 series. -- Add drm_bridge changes minimally without breaking existing code. -- Add new features for ptn3460, step-by-step. -- Address comments from Thierry and Andreas for ptn3460 and ps8622. -- Split documentation patches from driver patches.
I've tested your series on an Exynos5420 Peach Pit and an Exynos5250 Snow Chromebooks and display worked for me on both machines.
Great!
I also needed "[PATCH] drm/panel: simple: Add AUO B116XW03 panel support" [0] which does not apply cleanly on linux-next so you may want to do a re-spin for that patch.
Ok. I will take care of this in next version.
For Snow I also had to disable CONFIG_FB_SIMPLE, otherwise I just saw a blink on boot and only the backlight remained turned on (no display output). I don't know if that is expected since IIUC it should be possible to do a transition from simplefb to a DRM/KMS driver. I don't have a serial console hooked on this machine so I couldn't debug it further, sorry.
I am just wondering how SIMPLE FB can affect DRM based display. I am not even sure if both can co-exist or not. Is there anything we can do with bootargs instead of CONFIG?
I think the issue is that simplefb will register as console but the display driver is probably reconfiguring the display hardware and then registering a new console, so you'd end up with just a blank screen.
Typically the simplefb device tree nodes will be added by the bootloader so you might be able to prevent the bootloader from adding it. Ideally, though, the solution would be to do a proper hand-off from simplefb to DRM/KMS so that you can have a seemless transition. Somewhere in the middle would be to make simplefb unload when loading the DRM/KMS driver.
Thierry
dri-devel@lists.freedesktop.org