drm/omap: sdi: Register a drm_bridge
In order to integrate with a chain of drm_bridge, the internal SDI output has to expose its operations through the drm_bridge API. Register a bridge at initialisation time to do so and remove the omap_dss_device operations that are now unused. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ti.com> Tested-by: Sebastian Reichel <sebastian.reichel@collabora.com> Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com> Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-51-laurent.pinchart@ideasonboard.com
This commit is contained in:
Родитель
13d2d52f59
Коммит
8bef8a6d5d
|
@ -15,6 +15,8 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <drm/drm_bridge.h>
|
||||
|
||||
#include "dss.h"
|
||||
#include "omapdss.h"
|
||||
|
||||
|
@ -30,9 +32,11 @@ struct sdi_device {
|
|||
int datapairs;
|
||||
|
||||
struct omap_dss_device output;
|
||||
struct drm_bridge bridge;
|
||||
};
|
||||
|
||||
#define dssdev_to_sdi(dssdev) container_of(dssdev, struct sdi_device, output)
|
||||
#define drm_bridge_to_sdi(bridge) \
|
||||
container_of(bridge, struct sdi_device, bridge)
|
||||
|
||||
struct sdi_clk_calc_ctx {
|
||||
struct sdi_device *sdi;
|
||||
|
@ -118,9 +122,82 @@ static void sdi_config_lcd_manager(struct sdi_device *sdi)
|
|||
dss_mgr_set_lcd_config(&sdi->output, &sdi->mgr_config);
|
||||
}
|
||||
|
||||
static void sdi_display_enable(struct omap_dss_device *dssdev)
|
||||
/* -----------------------------------------------------------------------------
|
||||
* DRM Bridge Operations
|
||||
*/
|
||||
|
||||
static int sdi_bridge_attach(struct drm_bridge *bridge,
|
||||
enum drm_bridge_attach_flags flags)
|
||||
{
|
||||
struct sdi_device *sdi = dssdev_to_sdi(dssdev);
|
||||
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
||||
|
||||
if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
|
||||
return -EINVAL;
|
||||
|
||||
return drm_bridge_attach(bridge->encoder, sdi->output.next_bridge,
|
||||
bridge, flags);
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
sdi_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
||||
unsigned long pixelclock = mode->clock * 1000;
|
||||
struct dispc_clock_info dispc_cinfo;
|
||||
unsigned long fck;
|
||||
int ret;
|
||||
|
||||
if (pixelclock == 0)
|
||||
return MODE_NOCLOCK;
|
||||
|
||||
ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
|
||||
if (ret < 0)
|
||||
return MODE_CLOCK_RANGE;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static bool sdi_bridge_mode_fixup(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
||||
unsigned long pixelclock = mode->clock * 1000;
|
||||
struct dispc_clock_info dispc_cinfo;
|
||||
unsigned long fck;
|
||||
unsigned long pck;
|
||||
int ret;
|
||||
|
||||
ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
|
||||
if (ret < 0)
|
||||
return false;
|
||||
|
||||
pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
|
||||
|
||||
if (pck != pixelclock)
|
||||
dev_dbg(&sdi->pdev->dev,
|
||||
"pixel clock adjusted from %lu Hz to %lu Hz\n",
|
||||
pixelclock, pck);
|
||||
|
||||
adjusted_mode->clock = pck / 1000;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sdi_bridge_mode_set(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode,
|
||||
const struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
||||
|
||||
sdi->pixelclock = adjusted_mode->clock * 1000;
|
||||
}
|
||||
|
||||
static void sdi_bridge_enable(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state)
|
||||
{
|
||||
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
||||
struct dispc_clock_info dispc_cinfo;
|
||||
unsigned long fck;
|
||||
int r;
|
||||
|
@ -181,9 +258,10 @@ err_get_dispc:
|
|||
regulator_disable(sdi->vdds_sdi_reg);
|
||||
}
|
||||
|
||||
static void sdi_display_disable(struct omap_dss_device *dssdev)
|
||||
static void sdi_bridge_disable(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state)
|
||||
{
|
||||
struct sdi_device *sdi = dssdev_to_sdi(dssdev);
|
||||
struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
|
||||
|
||||
dss_mgr_disable(&sdi->output);
|
||||
|
||||
|
@ -194,71 +272,40 @@ static void sdi_display_disable(struct omap_dss_device *dssdev)
|
|||
regulator_disable(sdi->vdds_sdi_reg);
|
||||
}
|
||||
|
||||
static void sdi_set_timings(struct omap_dss_device *dssdev,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct sdi_device *sdi = dssdev_to_sdi(dssdev);
|
||||
|
||||
sdi->pixelclock = mode->clock * 1000;
|
||||
}
|
||||
|
||||
static int sdi_check_timings(struct omap_dss_device *dssdev,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct sdi_device *sdi = dssdev_to_sdi(dssdev);
|
||||
struct dispc_clock_info dispc_cinfo;
|
||||
unsigned long pixelclock = mode->clock * 1000;
|
||||
unsigned long fck;
|
||||
unsigned long pck;
|
||||
int r;
|
||||
|
||||
if (pixelclock == 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
|
||||
|
||||
if (pck != pixelclock) {
|
||||
DSSWARN("Pixel clock adjusted from %lu Hz to %lu Hz\n",
|
||||
pixelclock, pck);
|
||||
|
||||
mode->clock = pck / 1000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdi_connect(struct omap_dss_device *src,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
return omapdss_device_connect(dst->dss, dst, dst->next);
|
||||
}
|
||||
|
||||
static void sdi_disconnect(struct omap_dss_device *src,
|
||||
struct omap_dss_device *dst)
|
||||
{
|
||||
omapdss_device_disconnect(dst, dst->next);
|
||||
}
|
||||
|
||||
static const struct omap_dss_device_ops sdi_ops = {
|
||||
.connect = sdi_connect,
|
||||
.disconnect = sdi_disconnect,
|
||||
|
||||
.enable = sdi_display_enable,
|
||||
.disable = sdi_display_disable,
|
||||
|
||||
.check_timings = sdi_check_timings,
|
||||
.set_timings = sdi_set_timings,
|
||||
static const struct drm_bridge_funcs sdi_bridge_funcs = {
|
||||
.attach = sdi_bridge_attach,
|
||||
.mode_valid = sdi_bridge_mode_valid,
|
||||
.mode_fixup = sdi_bridge_mode_fixup,
|
||||
.mode_set = sdi_bridge_mode_set,
|
||||
.atomic_enable = sdi_bridge_enable,
|
||||
.atomic_disable = sdi_bridge_disable,
|
||||
};
|
||||
|
||||
static void sdi_bridge_init(struct sdi_device *sdi)
|
||||
{
|
||||
sdi->bridge.funcs = &sdi_bridge_funcs;
|
||||
sdi->bridge.of_node = sdi->pdev->dev.of_node;
|
||||
sdi->bridge.type = DRM_MODE_CONNECTOR_LVDS;
|
||||
|
||||
drm_bridge_add(&sdi->bridge);
|
||||
}
|
||||
|
||||
static void sdi_bridge_cleanup(struct sdi_device *sdi)
|
||||
{
|
||||
drm_bridge_remove(&sdi->bridge);
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialisation and Cleanup
|
||||
*/
|
||||
|
||||
static int sdi_init_output(struct sdi_device *sdi)
|
||||
{
|
||||
struct omap_dss_device *out = &sdi->output;
|
||||
int r;
|
||||
|
||||
sdi_bridge_init(sdi);
|
||||
|
||||
out->dev = &sdi->pdev->dev;
|
||||
out->id = OMAP_DSS_OUTPUT_SDI;
|
||||
out->type = OMAP_DISPLAY_TYPE_SDI;
|
||||
|
@ -266,14 +313,15 @@ static int sdi_init_output(struct sdi_device *sdi)
|
|||
out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
|
||||
/* We have SDI only on OMAP3, where it's on port 1 */
|
||||
out->of_port = 1;
|
||||
out->ops = &sdi_ops;
|
||||
out->owner = THIS_MODULE;
|
||||
out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE /* 15.5.9.1.2 */
|
||||
| DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE;
|
||||
|
||||
r = omapdss_device_init_output(out, NULL);
|
||||
if (r < 0)
|
||||
r = omapdss_device_init_output(out, &sdi->bridge);
|
||||
if (r < 0) {
|
||||
sdi_bridge_cleanup(sdi);
|
||||
return r;
|
||||
}
|
||||
|
||||
omapdss_device_register(out);
|
||||
|
||||
|
@ -284,6 +332,8 @@ static void sdi_uninit_output(struct sdi_device *sdi)
|
|||
{
|
||||
omapdss_device_unregister(&sdi->output);
|
||||
omapdss_device_cleanup_output(&sdi->output);
|
||||
|
||||
sdi_bridge_cleanup(sdi);
|
||||
}
|
||||
|
||||
int sdi_init_port(struct dss_device *dss, struct platform_device *pdev,
|
||||
|
|
Загрузка…
Ссылка в новой задаче