drm/omap: venc: Register a drm_bridge
In order to integrate with a chain of drm_bridge, the internal VENC encoder has to expose the mode valid, fixup and set, the enable and disable and the get modes operations through the drm_bridge API. Register a bridge at initialisation time to do so. Most of those operations are removed from the omap_dss_device as they are now called through the drm_bridge API by the DRM atomic helpers. The only exception is the .get_modes() operation that is still invoked through the omap_dss_device-based pipeline. For the time being make the next bridge in the chain optional as the VENC output is still based on omap_dss_device. The create_connector argument to the bridge attach function is also ignored for the same reason. This will be changed later when removing the related omapdrm-specific display drivers. 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-38-laurent.pinchart@ideasonboard.com
This commit is contained in:

committed by
Tomi Valkeinen

parent
4fcbfbae72
commit
2f004792ad
@@ -13,7 +13,6 @@
|
|||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
@@ -26,6 +25,8 @@
|
|||||||
#include <linux/component.h>
|
#include <linux/component.h>
|
||||||
#include <linux/sys_soc.h>
|
#include <linux/sys_soc.h>
|
||||||
|
|
||||||
|
#include <drm/drm_bridge.h>
|
||||||
|
|
||||||
#include "omapdss.h"
|
#include "omapdss.h"
|
||||||
#include "dss.h"
|
#include "dss.h"
|
||||||
|
|
||||||
@@ -289,7 +290,6 @@ static const struct drm_display_mode omap_dss_ntsc_mode = {
|
|||||||
struct venc_device {
|
struct venc_device {
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct mutex venc_lock;
|
|
||||||
struct regulator *vdda_dac_reg;
|
struct regulator *vdda_dac_reg;
|
||||||
struct dss_device *dss;
|
struct dss_device *dss;
|
||||||
|
|
||||||
@@ -303,9 +303,11 @@ struct venc_device {
|
|||||||
bool requires_tv_dac_clk;
|
bool requires_tv_dac_clk;
|
||||||
|
|
||||||
struct omap_dss_device output;
|
struct omap_dss_device output;
|
||||||
|
struct drm_bridge bridge;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define dssdev_to_venc(dssdev) container_of(dssdev, struct venc_device, output)
|
#define dssdev_to_venc(dssdev) container_of(dssdev, struct venc_device, output)
|
||||||
|
#define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge)
|
||||||
|
|
||||||
static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val)
|
static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val)
|
||||||
{
|
{
|
||||||
@@ -477,32 +479,6 @@ static void venc_power_off(struct venc_device *venc)
|
|||||||
venc_runtime_put(venc);
|
venc_runtime_put(venc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void venc_display_enable(struct omap_dss_device *dssdev)
|
|
||||||
{
|
|
||||||
struct venc_device *venc = dssdev_to_venc(dssdev);
|
|
||||||
|
|
||||||
DSSDBG("venc_display_enable\n");
|
|
||||||
|
|
||||||
mutex_lock(&venc->venc_lock);
|
|
||||||
|
|
||||||
venc_power_on(venc);
|
|
||||||
|
|
||||||
mutex_unlock(&venc->venc_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void venc_display_disable(struct omap_dss_device *dssdev)
|
|
||||||
{
|
|
||||||
struct venc_device *venc = dssdev_to_venc(dssdev);
|
|
||||||
|
|
||||||
DSSDBG("venc_display_disable\n");
|
|
||||||
|
|
||||||
mutex_lock(&venc->venc_lock);
|
|
||||||
|
|
||||||
venc_power_off(venc);
|
|
||||||
|
|
||||||
mutex_unlock(&venc->venc_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int venc_get_modes(struct omap_dss_device *dssdev,
|
static int venc_get_modes(struct omap_dss_device *dssdev,
|
||||||
struct drm_connector *connector)
|
struct drm_connector *connector)
|
||||||
{
|
{
|
||||||
@@ -545,57 +521,6 @@ static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mod
|
|||||||
return VENC_MODE_UNKNOWN;
|
return VENC_MODE_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void venc_set_timings(struct omap_dss_device *dssdev,
|
|
||||||
const struct drm_display_mode *mode)
|
|
||||||
{
|
|
||||||
struct venc_device *venc = dssdev_to_venc(dssdev);
|
|
||||||
enum venc_videomode venc_mode = venc_get_videomode(mode);
|
|
||||||
|
|
||||||
DSSDBG("venc_set_timings\n");
|
|
||||||
|
|
||||||
mutex_lock(&venc->venc_lock);
|
|
||||||
|
|
||||||
switch (venc_mode) {
|
|
||||||
default:
|
|
||||||
WARN_ON_ONCE(1);
|
|
||||||
/* Fall-through */
|
|
||||||
case VENC_MODE_PAL:
|
|
||||||
venc->config = &venc_config_pal_trm;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VENC_MODE_NTSC:
|
|
||||||
venc->config = &venc_config_ntsc_trm;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispc_set_tv_pclk(venc->dss->dispc, 13500000);
|
|
||||||
|
|
||||||
mutex_unlock(&venc->venc_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int venc_check_timings(struct omap_dss_device *dssdev,
|
|
||||||
struct drm_display_mode *mode)
|
|
||||||
{
|
|
||||||
DSSDBG("venc_check_timings\n");
|
|
||||||
|
|
||||||
switch (venc_get_videomode(mode)) {
|
|
||||||
case VENC_MODE_PAL:
|
|
||||||
drm_mode_copy(mode, &omap_dss_pal_mode);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VENC_MODE_NTSC:
|
|
||||||
drm_mode_copy(mode, &omap_dss_ntsc_mode);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
|
|
||||||
drm_mode_set_name(mode);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int venc_dump_regs(struct seq_file *s, void *p)
|
static int venc_dump_regs(struct seq_file *s, void *p)
|
||||||
{
|
{
|
||||||
struct venc_device *venc = s->private;
|
struct venc_device *venc = s->private;
|
||||||
@@ -689,15 +614,152 @@ static const struct omap_dss_device_ops venc_ops = {
|
|||||||
.connect = venc_connect,
|
.connect = venc_connect,
|
||||||
.disconnect = venc_disconnect,
|
.disconnect = venc_disconnect,
|
||||||
|
|
||||||
.enable = venc_display_enable,
|
|
||||||
.disable = venc_display_disable,
|
|
||||||
|
|
||||||
.check_timings = venc_check_timings,
|
|
||||||
.set_timings = venc_set_timings,
|
|
||||||
|
|
||||||
.get_modes = venc_get_modes,
|
.get_modes = venc_get_modes,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
* DRM Bridge Operations
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int venc_bridge_attach(struct drm_bridge *bridge,
|
||||||
|
enum drm_bridge_attach_flags flags)
|
||||||
|
{
|
||||||
|
struct venc_device *venc = drm_bridge_to_venc(bridge);
|
||||||
|
|
||||||
|
if (venc->output.next_bridge)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return drm_bridge_attach(bridge->encoder, venc->output.next_bridge,
|
||||||
|
bridge, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum drm_mode_status
|
||||||
|
venc_bridge_mode_valid(struct drm_bridge *bridge,
|
||||||
|
const struct drm_display_mode *mode)
|
||||||
|
{
|
||||||
|
switch (venc_get_videomode(mode)) {
|
||||||
|
case VENC_MODE_PAL:
|
||||||
|
case VENC_MODE_NTSC:
|
||||||
|
return MODE_OK;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return MODE_BAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool venc_bridge_mode_fixup(struct drm_bridge *bridge,
|
||||||
|
const struct drm_display_mode *mode,
|
||||||
|
struct drm_display_mode *adjusted_mode)
|
||||||
|
{
|
||||||
|
const struct drm_display_mode *venc_mode;
|
||||||
|
|
||||||
|
switch (venc_get_videomode(adjusted_mode)) {
|
||||||
|
case VENC_MODE_PAL:
|
||||||
|
venc_mode = &omap_dss_pal_mode;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VENC_MODE_NTSC:
|
||||||
|
venc_mode = &omap_dss_ntsc_mode;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
drm_mode_copy(adjusted_mode, venc_mode);
|
||||||
|
drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
|
||||||
|
drm_mode_set_name(adjusted_mode);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void venc_bridge_mode_set(struct drm_bridge *bridge,
|
||||||
|
const struct drm_display_mode *mode,
|
||||||
|
const struct drm_display_mode *adjusted_mode)
|
||||||
|
{
|
||||||
|
struct venc_device *venc = drm_bridge_to_venc(bridge);
|
||||||
|
enum venc_videomode venc_mode = venc_get_videomode(adjusted_mode);
|
||||||
|
|
||||||
|
switch (venc_mode) {
|
||||||
|
default:
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
/* Fall-through */
|
||||||
|
case VENC_MODE_PAL:
|
||||||
|
venc->config = &venc_config_pal_trm;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VENC_MODE_NTSC:
|
||||||
|
venc->config = &venc_config_ntsc_trm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispc_set_tv_pclk(venc->dss->dispc, 13500000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void venc_bridge_enable(struct drm_bridge *bridge)
|
||||||
|
{
|
||||||
|
struct venc_device *venc = drm_bridge_to_venc(bridge);
|
||||||
|
|
||||||
|
venc_power_on(venc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void venc_bridge_disable(struct drm_bridge *bridge)
|
||||||
|
{
|
||||||
|
struct venc_device *venc = drm_bridge_to_venc(bridge);
|
||||||
|
|
||||||
|
venc_power_off(venc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int venc_bridge_get_modes(struct drm_bridge *bridge,
|
||||||
|
struct drm_connector *connector)
|
||||||
|
{
|
||||||
|
static const struct drm_display_mode *modes[] = {
|
||||||
|
&omap_dss_pal_mode,
|
||||||
|
&omap_dss_ntsc_mode,
|
||||||
|
};
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(modes); ++i) {
|
||||||
|
struct drm_display_mode *mode;
|
||||||
|
|
||||||
|
mode = drm_mode_duplicate(connector->dev, modes[i]);
|
||||||
|
if (!mode)
|
||||||
|
return i;
|
||||||
|
|
||||||
|
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
|
||||||
|
drm_mode_set_name(mode);
|
||||||
|
drm_mode_probed_add(connector, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ARRAY_SIZE(modes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct drm_bridge_funcs venc_bridge_funcs = {
|
||||||
|
.attach = venc_bridge_attach,
|
||||||
|
.mode_valid = venc_bridge_mode_valid,
|
||||||
|
.mode_fixup = venc_bridge_mode_fixup,
|
||||||
|
.mode_set = venc_bridge_mode_set,
|
||||||
|
.enable = venc_bridge_enable,
|
||||||
|
.disable = venc_bridge_disable,
|
||||||
|
.get_modes = venc_bridge_get_modes,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void venc_bridge_init(struct venc_device *venc)
|
||||||
|
{
|
||||||
|
venc->bridge.funcs = &venc_bridge_funcs;
|
||||||
|
venc->bridge.of_node = venc->pdev->dev.of_node;
|
||||||
|
venc->bridge.ops = DRM_BRIDGE_OP_MODES;
|
||||||
|
venc->bridge.type = DRM_MODE_CONNECTOR_SVIDEO;
|
||||||
|
venc->bridge.interlace_allowed = true;
|
||||||
|
|
||||||
|
drm_bridge_add(&venc->bridge);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void venc_bridge_cleanup(struct venc_device *venc)
|
||||||
|
{
|
||||||
|
drm_bridge_remove(&venc->bridge);
|
||||||
|
}
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
* Component Bind & Unbind
|
* Component Bind & Unbind
|
||||||
*/
|
*/
|
||||||
@@ -747,6 +809,8 @@ static int venc_init_output(struct venc_device *venc)
|
|||||||
struct omap_dss_device *out = &venc->output;
|
struct omap_dss_device *out = &venc->output;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
venc_bridge_init(venc);
|
||||||
|
|
||||||
out->dev = &venc->pdev->dev;
|
out->dev = &venc->pdev->dev;
|
||||||
out->id = OMAP_DSS_OUTPUT_VENC;
|
out->id = OMAP_DSS_OUTPUT_VENC;
|
||||||
out->type = OMAP_DISPLAY_TYPE_VENC;
|
out->type = OMAP_DISPLAY_TYPE_VENC;
|
||||||
@@ -757,9 +821,11 @@ static int venc_init_output(struct venc_device *venc)
|
|||||||
out->of_port = 0;
|
out->of_port = 0;
|
||||||
out->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
|
out->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
|
||||||
|
|
||||||
r = omapdss_device_init_output(out, NULL);
|
r = omapdss_device_init_output(out, &venc->bridge);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
|
venc_bridge_cleanup(venc);
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
omapdss_device_register(out);
|
omapdss_device_register(out);
|
||||||
|
|
||||||
@@ -770,6 +836,8 @@ static void venc_uninit_output(struct venc_device *venc)
|
|||||||
{
|
{
|
||||||
omapdss_device_unregister(&venc->output);
|
omapdss_device_unregister(&venc->output);
|
||||||
omapdss_device_cleanup_output(&venc->output);
|
omapdss_device_cleanup_output(&venc->output);
|
||||||
|
|
||||||
|
venc_bridge_cleanup(venc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int venc_probe_of(struct venc_device *venc)
|
static int venc_probe_of(struct venc_device *venc)
|
||||||
@@ -839,8 +907,6 @@ static int venc_probe(struct platform_device *pdev)
|
|||||||
if (soc_device_match(venc_soc_devices))
|
if (soc_device_match(venc_soc_devices))
|
||||||
venc->requires_tv_dac_clk = true;
|
venc->requires_tv_dac_clk = true;
|
||||||
|
|
||||||
mutex_init(&venc->venc_lock);
|
|
||||||
|
|
||||||
venc->config = &venc_config_pal_trm;
|
venc->config = &venc_config_pal_trm;
|
||||||
|
|
||||||
venc_mem = platform_get_resource(venc->pdev, IORESOURCE_MEM, 0);
|
venc_mem = platform_get_resource(venc->pdev, IORESOURCE_MEM, 0);
|
||||||
|
Reference in New Issue
Block a user