mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
imx-drm updates
- atomic mode setting conversion - replace DMFC FIFO allocation mechanism with a fixed allocation that is good enough for all cases - support for external bridges connected to parallel-display - improved error handling in imx-ldb, imx-tve, and parallel-display - some code cleanup in imx-tve -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJXh1raAAoJEFDCiBxwnmDrWDIQALgkGU+38W6zspoNI1n317wy QiPk/B7j56Um2w/qw8IgAm+pXcps3g1cGVc0zXlXsF2Kft7FmbNPll5qTFrgXc+c odP2aOx4znFOPmMYuODKHL4GEG7F7qk1f/cVs4APZf0dupLEcxYsUHnTN/Z90hd7 JmPdbXMsC0ay/ZAvbtPTpf7BDkeUUF6vCM1JriJ0ngBlIq4LOocQOZGdA66zAnJM fxVxQ/HRGEIIn8YMwk+Rbbj6iVTl0quOcXchILtBB6JK0OR3J0HUKDOS1hzLI29E Y0kgZSDzQcE1U2BcHTtIlpa9hWqySVhv6tRFx2FVivy+H3wa2sQZ+rvpQKV6jNlx 0gme4b1GkX76oxhtg5nwj+Vhxazo1ANj+ezXJclr2FT2P3flGnzlBK9xQN68jOGy cJEGKcFrO7BbgF22UP3MKCHl6A74NuutFQ/SR9ZetspFtEmzmUeae8shhJw0FVUr ent5IeWeeZBC4KmtgIBtBwKM3PB/O7vmFVJGP47Y3uMR5qhzxUBQKTnoBIuFkgjz 3K1kDqR0AimDq+M9n8nnHXihYffFNtb4gBzbTaZTZUxYfsz8Uoe/kBpe2Gc8dcxq KYTCbJFZ49H7on5/wDD3P98m7rEnvWAcP3k/ZzS2tBhNTAaQprkq+tQL7+JFpXcV qTgSznrXDVlpgVXm4PnI =lWas -----END PGP SIGNATURE----- Merge tag 'imx-drm-next-2016-07-14' of git://git.pengutronix.de/git/pza/linux into drm-next imx-drm updates - atomic mode setting conversion - replace DMFC FIFO allocation mechanism with a fixed allocation that is good enough for all cases - support for external bridges connected to parallel-display - improved error handling in imx-ldb, imx-tve, and parallel-display - some code cleanup in imx-tve * tag 'imx-drm-next-2016-07-14' of git://git.pengutronix.de/git/pza/linux: drm/imx: parallel-display: add bridge support drm/imx: parallel-display: check return code from of_get_drm_display_mode() gpu: ipu-v3: ipu-dc: don't bug out on invalid bus_format drm/imx: imx-tve: fix the error message drm/imx: imx-tve: remove unneeded 'or' operation drm/imx: imx-tve: check the value returned by regulator_set_voltage() drm/imx: imx-ldb: check return code on panel attach drm/imx: turn remaining container_of macros into inline functions drm/imx: store internal bus configuration in crtc state drm/imx: remove empty mode_set encoder callbacks drm/imx: atomic phase 3 step 3: Advertise DRIVER_ATOMIC drm/imx: atomic phase 3 step 2: Legacy callback fixups drm/bridge: dw-hdmi: Remove the legacy drm_connector_funcs structure drm/imx: atomic phase 3 step 1: Use atomic configuration drm/imx: Remove encoders' ->prepare callbacks drm/imx: atomic phase 2 step 2: Track plane_state->fb correctly in ->page_flip drm/imx: atomic phase 2 step 1: Wire up state ->reset, ->duplicate and ->destroy drm/imx: atomic phase 1: Use transitional atomic CRTC and plane helpers gpu: ipu-v3: ipu-dmfc: Use static DMFC FIFO allocation mechanism drm/imx: ipuv3 plane: Check different types of plane separately
This commit is contained in:
commit
d3c35337fb
14 changed files with 803 additions and 1063 deletions
|
@ -1495,14 +1495,6 @@ static void dw_hdmi_connector_force(struct drm_connector *connector)
|
|||
}
|
||||
|
||||
static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = dw_hdmi_connector_detect,
|
||||
.destroy = dw_hdmi_connector_destroy,
|
||||
.force = dw_hdmi_connector_force,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs dw_hdmi_atomic_connector_funcs = {
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = dw_hdmi_connector_detect,
|
||||
|
@ -1634,14 +1626,9 @@ static int dw_hdmi_register(struct drm_device *drm, struct dw_hdmi *hdmi)
|
|||
drm_connector_helper_add(&hdmi->connector,
|
||||
&dw_hdmi_connector_helper_funcs);
|
||||
|
||||
if (drm_core_check_feature(drm, DRIVER_ATOMIC))
|
||||
drm_connector_init(drm, &hdmi->connector,
|
||||
&dw_hdmi_atomic_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA);
|
||||
else
|
||||
drm_connector_init(drm, &hdmi->connector,
|
||||
&dw_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA);
|
||||
drm_connector_init(drm, &hdmi->connector,
|
||||
&dw_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_HDMIA);
|
||||
|
||||
drm_mode_connector_attach_encoder(&hdmi->connector, encoder);
|
||||
|
||||
|
|
|
@ -28,6 +28,11 @@ struct imx_hdmi {
|
|||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static inline struct imx_hdmi *enc_to_imx_hdmi(struct drm_encoder *e)
|
||||
{
|
||||
return container_of(e, struct imx_hdmi, encoder);
|
||||
}
|
||||
|
||||
static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {
|
||||
{
|
||||
45250000, {
|
||||
|
@ -109,15 +114,9 @@ static void dw_hdmi_imx_encoder_disable(struct drm_encoder *encoder)
|
|||
{
|
||||
}
|
||||
|
||||
static void dw_hdmi_imx_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adj_mode)
|
||||
static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
}
|
||||
|
||||
static void dw_hdmi_imx_encoder_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
|
||||
struct imx_hdmi *hdmi = enc_to_imx_hdmi(encoder);
|
||||
int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder);
|
||||
|
||||
regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
|
||||
|
@ -125,16 +124,23 @@ static void dw_hdmi_imx_encoder_commit(struct drm_encoder *encoder)
|
|||
mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
|
||||
}
|
||||
|
||||
static void dw_hdmi_imx_encoder_prepare(struct drm_encoder *encoder)
|
||||
static int dw_hdmi_imx_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
imx_drm_set_bus_format(encoder, MEDIA_BUS_FMT_RGB888_1X24);
|
||||
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
|
||||
|
||||
imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
imx_crtc_state->di_hsync_pin = 2;
|
||||
imx_crtc_state->di_vsync_pin = 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {
|
||||
.mode_set = dw_hdmi_imx_encoder_mode_set,
|
||||
.prepare = dw_hdmi_imx_encoder_prepare,
|
||||
.commit = dw_hdmi_imx_encoder_commit,
|
||||
.enable = dw_hdmi_imx_encoder_enable,
|
||||
.disable = dw_hdmi_imx_encoder_disable,
|
||||
.atomic_check = dw_hdmi_imx_atomic_check,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
|
||||
|
|
|
@ -15,10 +15,14 @@
|
|||
*/
|
||||
#include <linux/component.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reservation.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
|
@ -41,6 +45,7 @@ struct imx_drm_device {
|
|||
struct imx_drm_crtc *crtc[MAX_CRTC];
|
||||
unsigned int pipes;
|
||||
struct drm_fbdev_cma *fbhelper;
|
||||
struct drm_atomic_state *state;
|
||||
};
|
||||
|
||||
struct imx_drm_crtc {
|
||||
|
@ -85,45 +90,6 @@ static int imx_drm_driver_unload(struct drm_device *drm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct imx_drm_crtc *imx_drm_find_crtc(struct drm_crtc *crtc)
|
||||
{
|
||||
struct imx_drm_device *imxdrm = crtc->dev->dev_private;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < MAX_CRTC; i++)
|
||||
if (imxdrm->crtc[i] && imxdrm->crtc[i]->crtc == crtc)
|
||||
return imxdrm->crtc[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int imx_drm_set_bus_config(struct drm_encoder *encoder, u32 bus_format,
|
||||
int hsync_pin, int vsync_pin, u32 bus_flags)
|
||||
{
|
||||
struct imx_drm_crtc_helper_funcs *helper;
|
||||
struct imx_drm_crtc *imx_crtc;
|
||||
|
||||
imx_crtc = imx_drm_find_crtc(encoder->crtc);
|
||||
if (!imx_crtc)
|
||||
return -EINVAL;
|
||||
|
||||
helper = &imx_crtc->imx_drm_helper_funcs;
|
||||
if (helper->set_interface_pix_fmt)
|
||||
return helper->set_interface_pix_fmt(encoder->crtc,
|
||||
bus_format, hsync_pin, vsync_pin,
|
||||
bus_flags);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_drm_set_bus_config);
|
||||
|
||||
int imx_drm_set_bus_format(struct drm_encoder *encoder, u32 bus_format)
|
||||
{
|
||||
return imx_drm_set_bus_config(encoder, bus_format, 2, 3,
|
||||
DRM_BUS_FLAG_DE_HIGH |
|
||||
DRM_BUS_FLAG_PIXDATA_NEGEDGE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(imx_drm_set_bus_format);
|
||||
|
||||
int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc)
|
||||
{
|
||||
return drm_crtc_vblank_get(imx_drm_crtc->crtc);
|
||||
|
@ -208,6 +174,63 @@ static void imx_drm_output_poll_changed(struct drm_device *drm)
|
|||
static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
|
||||
.fb_create = drm_fb_cma_create,
|
||||
.output_poll_changed = imx_drm_output_poll_changed,
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
};
|
||||
|
||||
static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *dev = state->dev;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
struct fence *excl;
|
||||
unsigned shared_count;
|
||||
struct fence **shared;
|
||||
unsigned int i, j;
|
||||
int ret;
|
||||
|
||||
/* Wait for fences. */
|
||||
for_each_crtc_in_state(state, crtc, crtc_state, i) {
|
||||
plane_state = crtc->primary->state;
|
||||
if (plane_state->fb) {
|
||||
cma_obj = drm_fb_cma_get_gem_obj(plane_state->fb, 0);
|
||||
if (cma_obj->base.dma_buf) {
|
||||
ret = reservation_object_get_fences_rcu(
|
||||
cma_obj->base.dma_buf->resv, &excl,
|
||||
&shared_count, &shared);
|
||||
if (unlikely(ret))
|
||||
DRM_ERROR("failed to get fences "
|
||||
"for buffer\n");
|
||||
|
||||
if (excl) {
|
||||
fence_wait(excl, false);
|
||||
fence_put(excl);
|
||||
}
|
||||
for (j = 0; j < shared_count; i++) {
|
||||
fence_wait(shared[j], false);
|
||||
fence_put(shared[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drm_atomic_helper_commit_modeset_disables(dev, state);
|
||||
|
||||
drm_atomic_helper_commit_planes(dev, state, true);
|
||||
|
||||
drm_atomic_helper_commit_modeset_enables(dev, state);
|
||||
|
||||
drm_atomic_helper_commit_hw_done(state);
|
||||
|
||||
drm_atomic_helper_wait_for_vblanks(dev, state);
|
||||
|
||||
drm_atomic_helper_cleanup_planes(dev, state);
|
||||
}
|
||||
|
||||
static struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = {
|
||||
.atomic_commit_tail = imx_drm_atomic_commit_tail,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -249,6 +272,7 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
|
|||
drm->mode_config.max_width = 4096;
|
||||
drm->mode_config.max_height = 4096;
|
||||
drm->mode_config.funcs = &imx_drm_mode_config_funcs;
|
||||
drm->mode_config.helper_private = &imx_drm_mode_config_helpers;
|
||||
|
||||
drm_mode_config_init(drm);
|
||||
|
||||
|
@ -279,6 +303,8 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
|
|||
}
|
||||
}
|
||||
|
||||
drm_mode_config_reset(drm);
|
||||
|
||||
/*
|
||||
* All components are now initialised, so setup the fb helper.
|
||||
* The fb helper takes copies of key hardware information, so the
|
||||
|
@ -289,7 +315,6 @@ static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
|
|||
dev_warn(drm->dev, "Invalid legacyfb_depth. Defaulting to 16bpp\n");
|
||||
legacyfb_depth = 16;
|
||||
}
|
||||
drm_helper_disable_unused_functions(drm);
|
||||
imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth,
|
||||
drm->mode_config.num_crtc, MAX_CRTC);
|
||||
if (IS_ERR(imxdrm->fbhelper)) {
|
||||
|
@ -403,7 +428,8 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = {
|
|||
};
|
||||
|
||||
static struct drm_driver imx_drm_driver = {
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
|
||||
DRIVER_ATOMIC,
|
||||
.load = imx_drm_driver_load,
|
||||
.unload = imx_drm_driver_unload,
|
||||
.lastclose = imx_drm_driver_lastclose,
|
||||
|
@ -491,6 +517,7 @@ static int imx_drm_platform_remove(struct platform_device *pdev)
|
|||
static int imx_drm_suspend(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(dev);
|
||||
struct imx_drm_device *imxdrm;
|
||||
|
||||
/* The drm_dev is NULL before .load hook is called */
|
||||
if (drm_dev == NULL)
|
||||
|
@ -498,17 +525,26 @@ static int imx_drm_suspend(struct device *dev)
|
|||
|
||||
drm_kms_helper_poll_disable(drm_dev);
|
||||
|
||||
imxdrm = drm_dev->dev_private;
|
||||
imxdrm->state = drm_atomic_helper_suspend(drm_dev);
|
||||
if (IS_ERR(imxdrm->state)) {
|
||||
drm_kms_helper_poll_enable(drm_dev);
|
||||
return PTR_ERR(imxdrm->state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_drm_resume(struct device *dev)
|
||||
{
|
||||
struct drm_device *drm_dev = dev_get_drvdata(dev);
|
||||
struct imx_drm_device *imx_drm;
|
||||
|
||||
if (drm_dev == NULL)
|
||||
return 0;
|
||||
|
||||
drm_helper_resume_force_mode(drm_dev);
|
||||
imx_drm = drm_dev->dev_private;
|
||||
drm_atomic_helper_resume(drm_dev, imx_drm->state);
|
||||
drm_kms_helper_poll_enable(drm_dev);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -15,12 +15,22 @@ struct platform_device;
|
|||
|
||||
unsigned int imx_drm_crtc_id(struct imx_drm_crtc *crtc);
|
||||
|
||||
struct imx_crtc_state {
|
||||
struct drm_crtc_state base;
|
||||
u32 bus_format;
|
||||
u32 bus_flags;
|
||||
int di_hsync_pin;
|
||||
int di_vsync_pin;
|
||||
};
|
||||
|
||||
static inline struct imx_crtc_state *to_imx_crtc_state(struct drm_crtc_state *s)
|
||||
{
|
||||
return container_of(s, struct imx_crtc_state, base);
|
||||
}
|
||||
|
||||
struct imx_drm_crtc_helper_funcs {
|
||||
int (*enable_vblank)(struct drm_crtc *crtc);
|
||||
void (*disable_vblank)(struct drm_crtc *crtc);
|
||||
int (*set_interface_pix_fmt)(struct drm_crtc *crtc,
|
||||
u32 bus_format, int hsync_pin, int vsync_pin,
|
||||
u32 bus_flags);
|
||||
const struct drm_crtc_helper_funcs *crtc_helper_funcs;
|
||||
const struct drm_crtc_funcs *crtc_funcs;
|
||||
};
|
||||
|
@ -42,11 +52,6 @@ void imx_drm_mode_config_init(struct drm_device *drm);
|
|||
|
||||
struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb);
|
||||
|
||||
int imx_drm_set_bus_config(struct drm_encoder *encoder, u32 bus_format,
|
||||
int hsync_pin, int vsync_pin, u32 bus_flags);
|
||||
int imx_drm_set_bus_format(struct drm_encoder *encoder,
|
||||
u32 bus_format);
|
||||
|
||||
int imx_drm_encoder_parse_of(struct drm_device *drm,
|
||||
struct drm_encoder *encoder, struct device_node *np);
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_of.h>
|
||||
|
@ -49,9 +51,6 @@
|
|||
#define LDB_DI1_VS_POL_ACT_LOW (1 << 10)
|
||||
#define LDB_BGREF_RMODE_INT (1 << 15)
|
||||
|
||||
#define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector)
|
||||
#define enc_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, encoder)
|
||||
|
||||
struct imx_ldb;
|
||||
|
||||
struct imx_ldb_channel {
|
||||
|
@ -66,9 +65,19 @@ struct imx_ldb_channel {
|
|||
int edid_len;
|
||||
struct drm_display_mode mode;
|
||||
int mode_valid;
|
||||
int bus_format;
|
||||
u32 bus_format;
|
||||
};
|
||||
|
||||
static inline struct imx_ldb_channel *con_to_imx_ldb_ch(struct drm_connector *c)
|
||||
{
|
||||
return container_of(c, struct imx_ldb_channel, connector);
|
||||
}
|
||||
|
||||
static inline struct imx_ldb_channel *enc_to_imx_ldb_ch(struct drm_encoder *e)
|
||||
{
|
||||
return container_of(e, struct imx_ldb_channel, encoder);
|
||||
}
|
||||
|
||||
struct bus_mux {
|
||||
int reg;
|
||||
int shift;
|
||||
|
@ -93,6 +102,32 @@ static enum drm_connector_status imx_ldb_connector_detect(
|
|||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static void imx_ldb_ch_set_bus_format(struct imx_ldb_channel *imx_ldb_ch,
|
||||
u32 bus_format)
|
||||
{
|
||||
struct imx_ldb *ldb = imx_ldb_ch->ldb;
|
||||
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
|
||||
|
||||
switch (bus_format) {
|
||||
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
|
||||
if (imx_ldb_ch->chno == 0 || dual)
|
||||
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
|
||||
if (imx_ldb_ch->chno == 1 || dual)
|
||||
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
|
||||
if (imx_ldb_ch->chno == 0 || dual)
|
||||
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |
|
||||
LDB_BIT_MAP_CH0_JEIDA;
|
||||
if (imx_ldb_ch->chno == 1 || dual)
|
||||
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |
|
||||
LDB_BIT_MAP_CH1_JEIDA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int imx_ldb_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
|
||||
|
@ -100,11 +135,7 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector)
|
|||
|
||||
if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs &&
|
||||
imx_ldb_ch->panel->funcs->get_modes) {
|
||||
struct drm_display_info *di = &connector->display_info;
|
||||
|
||||
num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel);
|
||||
if (!imx_ldb_ch->bus_format && di->num_bus_formats)
|
||||
imx_ldb_ch->bus_format = di->bus_formats[0];
|
||||
if (num_modes > 0)
|
||||
return num_modes;
|
||||
}
|
||||
|
@ -141,10 +172,6 @@ static struct drm_encoder *imx_ldb_connector_best_encoder(
|
|||
return &imx_ldb_ch->encoder;
|
||||
}
|
||||
|
||||
static void imx_ldb_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
}
|
||||
|
||||
static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
|
||||
unsigned long serial_clk, unsigned long di_clk)
|
||||
{
|
||||
|
@ -173,43 +200,7 @@ static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
|
|||
chno);
|
||||
}
|
||||
|
||||
static void imx_ldb_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
|
||||
struct imx_ldb *ldb = imx_ldb_ch->ldb;
|
||||
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
|
||||
u32 bus_format;
|
||||
|
||||
switch (imx_ldb_ch->bus_format) {
|
||||
default:
|
||||
dev_warn(ldb->dev,
|
||||
"could not determine data mapping, default to 18-bit \"spwg\"\n");
|
||||
/* fallthrough */
|
||||
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
|
||||
bus_format = MEDIA_BUS_FMT_RGB666_1X18;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
|
||||
bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
if (imx_ldb_ch->chno == 0 || dual)
|
||||
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
|
||||
if (imx_ldb_ch->chno == 1 || dual)
|
||||
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
|
||||
bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
if (imx_ldb_ch->chno == 0 || dual)
|
||||
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |
|
||||
LDB_BIT_MAP_CH0_JEIDA;
|
||||
if (imx_ldb_ch->chno == 1 || dual)
|
||||
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |
|
||||
LDB_BIT_MAP_CH1_JEIDA;
|
||||
break;
|
||||
}
|
||||
|
||||
imx_drm_set_bus_format(encoder, bus_format);
|
||||
}
|
||||
|
||||
static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
|
||||
static void imx_ldb_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
|
||||
struct imx_ldb *ldb = imx_ldb_ch->ldb;
|
||||
|
@ -219,8 +210,13 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder)
|
|||
drm_panel_prepare(imx_ldb_ch->panel);
|
||||
|
||||
if (dual) {
|
||||
clk_set_parent(ldb->clk_sel[mux], ldb->clk[0]);
|
||||
clk_set_parent(ldb->clk_sel[mux], ldb->clk[1]);
|
||||
|
||||
clk_prepare_enable(ldb->clk[0]);
|
||||
clk_prepare_enable(ldb->clk[1]);
|
||||
} else {
|
||||
clk_set_parent(ldb->clk_sel[mux], ldb->clk[imx_ldb_ch->chno]);
|
||||
}
|
||||
|
||||
if (imx_ldb_ch == &ldb->channel[0] || dual) {
|
||||
|
@ -265,6 +261,7 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
|
|||
unsigned long serial_clk;
|
||||
unsigned long di_clk = mode->clock * 1000;
|
||||
int mux = drm_of_encoder_active_port_id(imx_ldb_ch->child, encoder);
|
||||
u32 bus_format = imx_ldb_ch->bus_format;
|
||||
|
||||
if (mode->clock > 170000) {
|
||||
dev_warn(ldb->dev,
|
||||
|
@ -286,18 +283,36 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
|
|||
}
|
||||
|
||||
/* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
|
||||
if (imx_ldb_ch == &ldb->channel[0]) {
|
||||
if (imx_ldb_ch == &ldb->channel[0] || dual) {
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
|
||||
else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW;
|
||||
}
|
||||
if (imx_ldb_ch == &ldb->channel[1]) {
|
||||
if (imx_ldb_ch == &ldb->channel[1] || dual) {
|
||||
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
|
||||
else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW;
|
||||
}
|
||||
|
||||
if (!bus_format) {
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_connector *connector;
|
||||
int i;
|
||||
|
||||
for_each_connector_in_state(encoder->crtc->state->state,
|
||||
connector, conn_state, i) {
|
||||
struct drm_display_info *di = &connector->display_info;
|
||||
|
||||
if (conn_state->crtc == encoder->crtc &&
|
||||
di->num_bus_formats) {
|
||||
bus_format = di->bus_formats[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
imx_ldb_ch_set_bus_format(imx_ldb_ch, bus_format);
|
||||
}
|
||||
|
||||
static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
|
||||
|
@ -357,11 +372,45 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
|
|||
drm_panel_unprepare(imx_ldb_ch->panel);
|
||||
}
|
||||
|
||||
static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
|
||||
struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
|
||||
struct drm_display_info *di = &conn_state->connector->display_info;
|
||||
u32 bus_format = imx_ldb_ch->bus_format;
|
||||
|
||||
/* Bus format description in DT overrides connector display info. */
|
||||
if (!bus_format && di->num_bus_formats)
|
||||
bus_format = di->bus_formats[0];
|
||||
switch (bus_format) {
|
||||
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
|
||||
imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
|
||||
break;
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
|
||||
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
|
||||
imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
imx_crtc_state->di_hsync_pin = 2;
|
||||
imx_crtc_state->di_vsync_pin = 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct drm_connector_funcs imx_ldb_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = imx_ldb_connector_detect,
|
||||
.destroy = imx_drm_connector_destroy,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs imx_ldb_connector_helper_funcs = {
|
||||
|
@ -374,11 +423,10 @@ static const struct drm_encoder_funcs imx_ldb_encoder_funcs = {
|
|||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
|
||||
.dpms = imx_ldb_encoder_dpms,
|
||||
.prepare = imx_ldb_encoder_prepare,
|
||||
.commit = imx_ldb_encoder_commit,
|
||||
.mode_set = imx_ldb_encoder_mode_set,
|
||||
.enable = imx_ldb_encoder_enable,
|
||||
.disable = imx_ldb_encoder_disable,
|
||||
.atomic_check = imx_ldb_encoder_atomic_check,
|
||||
};
|
||||
|
||||
static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno)
|
||||
|
@ -400,10 +448,10 @@ static int imx_ldb_register(struct drm_device *drm,
|
|||
struct imx_ldb_channel *imx_ldb_ch)
|
||||
{
|
||||
struct imx_ldb *ldb = imx_ldb_ch->ldb;
|
||||
struct drm_encoder *encoder = &imx_ldb_ch->encoder;
|
||||
int ret;
|
||||
|
||||
ret = imx_drm_encoder_parse_of(drm, &imx_ldb_ch->encoder,
|
||||
imx_ldb_ch->child);
|
||||
ret = imx_drm_encoder_parse_of(drm, encoder, imx_ldb_ch->child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -417,9 +465,8 @@ static int imx_ldb_register(struct drm_device *drm,
|
|||
return ret;
|
||||
}
|
||||
|
||||
drm_encoder_helper_add(&imx_ldb_ch->encoder,
|
||||
&imx_ldb_encoder_helper_funcs);
|
||||
drm_encoder_init(drm, &imx_ldb_ch->encoder, &imx_ldb_encoder_funcs,
|
||||
drm_encoder_helper_add(encoder, &imx_ldb_encoder_helper_funcs);
|
||||
drm_encoder_init(drm, encoder, &imx_ldb_encoder_funcs,
|
||||
DRM_MODE_ENCODER_LVDS, NULL);
|
||||
|
||||
drm_connector_helper_add(&imx_ldb_ch->connector,
|
||||
|
@ -427,11 +474,14 @@ static int imx_ldb_register(struct drm_device *drm,
|
|||
drm_connector_init(drm, &imx_ldb_ch->connector,
|
||||
&imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
|
||||
|
||||
if (imx_ldb_ch->panel)
|
||||
drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector);
|
||||
if (imx_ldb_ch->panel) {
|
||||
ret = drm_panel_attach(imx_ldb_ch->panel,
|
||||
&imx_ldb_ch->connector);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
|
||||
&imx_ldb_ch->encoder);
|
||||
drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, encoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -560,6 +610,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
|
|||
struct imx_ldb_channel *channel;
|
||||
struct device_node *ddc_node;
|
||||
struct device_node *ep;
|
||||
int bus_format;
|
||||
|
||||
ret = of_property_read_u32(child, "reg", &i);
|
||||
if (ret || i < 0 || i > 1)
|
||||
|
@ -632,21 +683,22 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
|
|||
}
|
||||
}
|
||||
|
||||
channel->bus_format = of_get_bus_format(dev, child);
|
||||
if (channel->bus_format == -EINVAL) {
|
||||
bus_format = of_get_bus_format(dev, child);
|
||||
if (bus_format == -EINVAL) {
|
||||
/*
|
||||
* If no bus format was specified in the device tree,
|
||||
* we can still get it from the connected panel later.
|
||||
*/
|
||||
if (channel->panel && channel->panel->funcs &&
|
||||
channel->panel->funcs->get_modes)
|
||||
channel->bus_format = 0;
|
||||
bus_format = 0;
|
||||
}
|
||||
if (channel->bus_format < 0) {
|
||||
if (bus_format < 0) {
|
||||
dev_err(dev, "could not determine data mapping: %d\n",
|
||||
channel->bus_format);
|
||||
return channel->bus_format;
|
||||
bus_format);
|
||||
return bus_format;
|
||||
}
|
||||
channel->bus_format = bus_format;
|
||||
|
||||
ret = imx_ldb_register(drm, channel);
|
||||
if (ret)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <video/imx-ipu-v3.h>
|
||||
|
@ -97,9 +98,6 @@
|
|||
/* TVE_TST_MODE_REG */
|
||||
#define TVE_TVDAC_TEST_MODE_MASK (0x7 << 0)
|
||||
|
||||
#define con_to_tve(x) container_of(x, struct imx_tve, connector)
|
||||
#define enc_to_tve(x) container_of(x, struct imx_tve, encoder)
|
||||
|
||||
enum {
|
||||
TVE_MODE_TVOUT,
|
||||
TVE_MODE_VGA,
|
||||
|
@ -112,6 +110,8 @@ struct imx_tve {
|
|||
spinlock_t lock; /* register lock */
|
||||
bool enabled;
|
||||
int mode;
|
||||
int di_hsync_pin;
|
||||
int di_vsync_pin;
|
||||
|
||||
struct regmap *regmap;
|
||||
struct regulator *dac_reg;
|
||||
|
@ -120,10 +120,18 @@ struct imx_tve {
|
|||
struct clk *di_sel_clk;
|
||||
struct clk_hw clk_hw_di;
|
||||
struct clk *di_clk;
|
||||
int vsync_pin;
|
||||
int hsync_pin;
|
||||
};
|
||||
|
||||
static inline struct imx_tve *con_to_tve(struct drm_connector *c)
|
||||
{
|
||||
return container_of(c, struct imx_tve, connector);
|
||||
}
|
||||
|
||||
static inline struct imx_tve *enc_to_tve(struct drm_encoder *e)
|
||||
{
|
||||
return container_of(e, struct imx_tve, encoder);
|
||||
}
|
||||
|
||||
static void tve_lock(void *__tve)
|
||||
__acquires(&tve->lock)
|
||||
{
|
||||
|
@ -148,8 +156,7 @@ static void tve_enable(struct imx_tve *tve)
|
|||
tve->enabled = true;
|
||||
clk_prepare_enable(tve->clk);
|
||||
ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG,
|
||||
TVE_IPU_CLK_EN | TVE_EN,
|
||||
TVE_IPU_CLK_EN | TVE_EN);
|
||||
TVE_EN, TVE_EN);
|
||||
}
|
||||
|
||||
/* clear interrupt status register */
|
||||
|
@ -172,7 +179,7 @@ static void tve_disable(struct imx_tve *tve)
|
|||
if (tve->enabled) {
|
||||
tve->enabled = false;
|
||||
ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG,
|
||||
TVE_IPU_CLK_EN | TVE_EN, 0);
|
||||
TVE_EN, 0);
|
||||
clk_disable_unprepare(tve->clk);
|
||||
}
|
||||
}
|
||||
|
@ -275,36 +282,6 @@ static struct drm_encoder *imx_tve_connector_best_encoder(
|
|||
return &tve->encoder;
|
||||
}
|
||||
|
||||
static void imx_tve_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct imx_tve *tve = enc_to_tve(encoder);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(tve->regmap, TVE_COM_CONF_REG,
|
||||
TVE_TV_OUT_MODE_MASK, TVE_TV_OUT_DISABLE);
|
||||
if (ret < 0)
|
||||
dev_err(tve->dev, "failed to disable TVOUT: %d\n", ret);
|
||||
}
|
||||
|
||||
static void imx_tve_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct imx_tve *tve = enc_to_tve(encoder);
|
||||
|
||||
tve_disable(tve);
|
||||
|
||||
switch (tve->mode) {
|
||||
case TVE_MODE_VGA:
|
||||
imx_drm_set_bus_config(encoder, MEDIA_BUS_FMT_GBR888_1X24,
|
||||
tve->hsync_pin, tve->vsync_pin,
|
||||
DRM_BUS_FLAG_DE_HIGH |
|
||||
DRM_BUS_FLAG_PIXDATA_NEGEDGE);
|
||||
break;
|
||||
case TVE_MODE_TVOUT:
|
||||
imx_drm_set_bus_format(encoder, MEDIA_BUS_FMT_YUV8_1X24);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void imx_tve_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *orig_mode,
|
||||
struct drm_display_mode *mode)
|
||||
|
@ -333,6 +310,9 @@ static void imx_tve_encoder_mode_set(struct drm_encoder *encoder,
|
|||
ret);
|
||||
}
|
||||
|
||||
regmap_update_bits(tve->regmap, TVE_COM_CONF_REG,
|
||||
TVE_IPU_CLK_EN, TVE_IPU_CLK_EN);
|
||||
|
||||
if (tve->mode == TVE_MODE_VGA)
|
||||
ret = tve_setup_vga(tve);
|
||||
else
|
||||
|
@ -341,7 +321,7 @@ static void imx_tve_encoder_mode_set(struct drm_encoder *encoder,
|
|||
dev_err(tve->dev, "failed to set configuration: %d\n", ret);
|
||||
}
|
||||
|
||||
static void imx_tve_encoder_commit(struct drm_encoder *encoder)
|
||||
static void imx_tve_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct imx_tve *tve = enc_to_tve(encoder);
|
||||
|
||||
|
@ -355,11 +335,28 @@ static void imx_tve_encoder_disable(struct drm_encoder *encoder)
|
|||
tve_disable(tve);
|
||||
}
|
||||
|
||||
static int imx_tve_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
|
||||
struct imx_tve *tve = enc_to_tve(encoder);
|
||||
|
||||
imx_crtc_state->bus_format = MEDIA_BUS_FMT_GBR888_1X24;
|
||||
imx_crtc_state->di_hsync_pin = tve->di_hsync_pin;
|
||||
imx_crtc_state->di_vsync_pin = tve->di_vsync_pin;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs imx_tve_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = imx_tve_connector_detect,
|
||||
.destroy = imx_drm_connector_destroy,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs imx_tve_connector_helper_funcs = {
|
||||
|
@ -373,11 +370,10 @@ static const struct drm_encoder_funcs imx_tve_encoder_funcs = {
|
|||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs imx_tve_encoder_helper_funcs = {
|
||||
.dpms = imx_tve_encoder_dpms,
|
||||
.prepare = imx_tve_encoder_prepare,
|
||||
.mode_set = imx_tve_encoder_mode_set,
|
||||
.commit = imx_tve_encoder_commit,
|
||||
.enable = imx_tve_encoder_enable,
|
||||
.disable = imx_tve_encoder_disable,
|
||||
.atomic_check = imx_tve_atomic_check,
|
||||
};
|
||||
|
||||
static irqreturn_t imx_tve_irq_handler(int irq, void *data)
|
||||
|
@ -495,8 +491,7 @@ static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve)
|
|||
encoder_type = tve->mode == TVE_MODE_VGA ?
|
||||
DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC;
|
||||
|
||||
ret = imx_drm_encoder_parse_of(drm, &tve->encoder,
|
||||
tve->dev->of_node);
|
||||
ret = imx_drm_encoder_parse_of(drm, &tve->encoder, tve->dev->of_node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -587,15 +582,15 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
|
|||
|
||||
if (tve->mode == TVE_MODE_VGA) {
|
||||
ret = of_property_read_u32(np, "fsl,hsync-pin",
|
||||
&tve->hsync_pin);
|
||||
&tve->di_hsync_pin);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get vsync pin\n");
|
||||
dev_err(dev, "failed to get hsync pin\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret |= of_property_read_u32(np, "fsl,vsync-pin",
|
||||
&tve->vsync_pin);
|
||||
ret = of_property_read_u32(np, "fsl,vsync-pin",
|
||||
&tve->di_vsync_pin);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get vsync pin\n");
|
||||
|
@ -633,7 +628,9 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
|
|||
|
||||
tve->dac_reg = devm_regulator_get(dev, "dac");
|
||||
if (!IS_ERR(tve->dac_reg)) {
|
||||
regulator_set_voltage(tve->dac_reg, 2750000, 2750000);
|
||||
ret = regulator_set_voltage(tve->dac_reg, 2750000, 2750000);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regulator_enable(tve->dac_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -18,12 +18,12 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/reservation.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
|
||||
|
@ -33,23 +33,6 @@
|
|||
|
||||
#define DRIVER_DESC "i.MX IPUv3 Graphics"
|
||||
|
||||
enum ipu_flip_status {
|
||||
IPU_FLIP_NONE,
|
||||
IPU_FLIP_PENDING,
|
||||
IPU_FLIP_SUBMITTED,
|
||||
};
|
||||
|
||||
struct ipu_flip_work {
|
||||
struct work_struct unref_work;
|
||||
struct drm_gem_object *bo;
|
||||
struct drm_pending_vblank_event *page_flip_event;
|
||||
struct work_struct fence_work;
|
||||
struct ipu_crtc *crtc;
|
||||
struct fence *excl;
|
||||
unsigned shared_count;
|
||||
struct fence **shared;
|
||||
};
|
||||
|
||||
struct ipu_crtc {
|
||||
struct device *dev;
|
||||
struct drm_crtc base;
|
||||
|
@ -60,284 +43,99 @@ struct ipu_crtc {
|
|||
|
||||
struct ipu_dc *dc;
|
||||
struct ipu_di *di;
|
||||
int enabled;
|
||||
enum ipu_flip_status flip_state;
|
||||
struct workqueue_struct *flip_queue;
|
||||
struct ipu_flip_work *flip_work;
|
||||
int irq;
|
||||
u32 bus_format;
|
||||
u32 bus_flags;
|
||||
int di_hsync_pin;
|
||||
int di_vsync_pin;
|
||||
};
|
||||
|
||||
#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)
|
||||
|
||||
static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
|
||||
static inline struct ipu_crtc *to_ipu_crtc(struct drm_crtc *crtc)
|
||||
{
|
||||
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
|
||||
return container_of(crtc, struct ipu_crtc, base);
|
||||
}
|
||||
|
||||
if (ipu_crtc->enabled)
|
||||
return;
|
||||
static void ipu_crtc_enable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
|
||||
|
||||
ipu_dc_enable(ipu);
|
||||
ipu_plane_enable(ipu_crtc->plane[0]);
|
||||
/* Start DC channel and DI after IDMAC */
|
||||
ipu_dc_enable_channel(ipu_crtc->dc);
|
||||
ipu_di_enable(ipu_crtc->di);
|
||||
drm_crtc_vblank_on(&ipu_crtc->base);
|
||||
|
||||
ipu_crtc->enabled = 1;
|
||||
}
|
||||
|
||||
static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
|
||||
static void ipu_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
|
||||
|
||||
if (!ipu_crtc->enabled)
|
||||
return;
|
||||
|
||||
/* Stop DC channel and DI before IDMAC */
|
||||
ipu_dc_disable_channel(ipu_crtc->dc);
|
||||
ipu_di_disable(ipu_crtc->di);
|
||||
ipu_plane_disable(ipu_crtc->plane[0]);
|
||||
ipu_dc_disable(ipu);
|
||||
drm_crtc_vblank_off(&ipu_crtc->base);
|
||||
|
||||
ipu_crtc->enabled = 0;
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
if (crtc->state->event) {
|
||||
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
}
|
||||
|
||||
static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
static void imx_drm_crtc_reset(struct drm_crtc *crtc)
|
||||
{
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
struct imx_crtc_state *state;
|
||||
|
||||
dev_dbg(ipu_crtc->dev, "%s mode: %d\n", __func__, mode);
|
||||
if (crtc->state) {
|
||||
if (crtc->state->mode_blob)
|
||||
drm_property_unreference_blob(crtc->state->mode_blob);
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
ipu_fb_enable(ipu_crtc);
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
ipu_fb_disable(ipu_crtc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipu_flip_unref_work_func(struct work_struct *__work)
|
||||
{
|
||||
struct ipu_flip_work *work =
|
||||
container_of(__work, struct ipu_flip_work, unref_work);
|
||||
|
||||
drm_gem_object_unreference_unlocked(work->bo);
|
||||
kfree(work);
|
||||
}
|
||||
|
||||
static void ipu_flip_fence_work_func(struct work_struct *__work)
|
||||
{
|
||||
struct ipu_flip_work *work =
|
||||
container_of(__work, struct ipu_flip_work, fence_work);
|
||||
int i;
|
||||
|
||||
/* wait for all fences attached to the FB obj to signal */
|
||||
if (work->excl) {
|
||||
fence_wait(work->excl, false);
|
||||
fence_put(work->excl);
|
||||
}
|
||||
for (i = 0; i < work->shared_count; i++) {
|
||||
fence_wait(work->shared[i], false);
|
||||
fence_put(work->shared[i]);
|
||||
}
|
||||
|
||||
work->crtc->flip_state = IPU_FLIP_SUBMITTED;
|
||||
}
|
||||
|
||||
static int ipu_page_flip(struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb,
|
||||
struct drm_pending_vblank_event *event,
|
||||
uint32_t page_flip_flags)
|
||||
{
|
||||
struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
struct ipu_flip_work *flip_work;
|
||||
int ret;
|
||||
|
||||
if (ipu_crtc->flip_state != IPU_FLIP_NONE)
|
||||
return -EBUSY;
|
||||
|
||||
ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc);
|
||||
if (ret) {
|
||||
dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n");
|
||||
list_del(&event->base.link);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
flip_work = kzalloc(sizeof *flip_work, GFP_KERNEL);
|
||||
if (!flip_work) {
|
||||
ret = -ENOMEM;
|
||||
goto put_vblank;
|
||||
}
|
||||
INIT_WORK(&flip_work->unref_work, ipu_flip_unref_work_func);
|
||||
flip_work->page_flip_event = event;
|
||||
|
||||
/* get BO backing the old framebuffer and take a reference */
|
||||
flip_work->bo = &drm_fb_cma_get_gem_obj(crtc->primary->fb, 0)->base;
|
||||
drm_gem_object_reference(flip_work->bo);
|
||||
|
||||
ipu_crtc->flip_work = flip_work;
|
||||
/*
|
||||
* If the object has a DMABUF attached, we need to wait on its fences
|
||||
* if there are any.
|
||||
*/
|
||||
if (cma_obj->base.dma_buf) {
|
||||
INIT_WORK(&flip_work->fence_work, ipu_flip_fence_work_func);
|
||||
flip_work->crtc = ipu_crtc;
|
||||
|
||||
ret = reservation_object_get_fences_rcu(
|
||||
cma_obj->base.dma_buf->resv, &flip_work->excl,
|
||||
&flip_work->shared_count, &flip_work->shared);
|
||||
|
||||
if (unlikely(ret)) {
|
||||
DRM_ERROR("failed to get fences for buffer\n");
|
||||
goto free_flip_work;
|
||||
}
|
||||
|
||||
/* No need to queue the worker if the are no fences */
|
||||
if (!flip_work->excl && !flip_work->shared_count) {
|
||||
ipu_crtc->flip_state = IPU_FLIP_SUBMITTED;
|
||||
} else {
|
||||
ipu_crtc->flip_state = IPU_FLIP_PENDING;
|
||||
queue_work(ipu_crtc->flip_queue,
|
||||
&flip_work->fence_work);
|
||||
}
|
||||
state = to_imx_crtc_state(crtc->state);
|
||||
memset(state, 0, sizeof(*state));
|
||||
} else {
|
||||
ipu_crtc->flip_state = IPU_FLIP_SUBMITTED;
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (!state)
|
||||
return;
|
||||
crtc->state = &state->base;
|
||||
}
|
||||
|
||||
return 0;
|
||||
state->base.crtc = crtc;
|
||||
}
|
||||
|
||||
free_flip_work:
|
||||
drm_gem_object_unreference_unlocked(flip_work->bo);
|
||||
kfree(flip_work);
|
||||
ipu_crtc->flip_work = NULL;
|
||||
put_vblank:
|
||||
imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
|
||||
static struct drm_crtc_state *imx_drm_crtc_duplicate_state(struct drm_crtc *crtc)
|
||||
{
|
||||
struct imx_crtc_state *state;
|
||||
|
||||
return ret;
|
||||
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
__drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
|
||||
|
||||
WARN_ON(state->base.crtc != crtc);
|
||||
state->base.crtc = crtc;
|
||||
|
||||
return &state->base;
|
||||
}
|
||||
|
||||
static void imx_drm_crtc_destroy_state(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
__drm_atomic_helper_crtc_destroy_state(state);
|
||||
kfree(to_imx_crtc_state(state));
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs ipu_crtc_funcs = {
|
||||
.set_config = drm_crtc_helper_set_config,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
.destroy = drm_crtc_cleanup,
|
||||
.page_flip = ipu_page_flip,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.reset = imx_drm_crtc_reset,
|
||||
.atomic_duplicate_state = imx_drm_crtc_duplicate_state,
|
||||
.atomic_destroy_state = imx_drm_crtc_destroy_state,
|
||||
};
|
||||
|
||||
static int ipu_crtc_mode_set(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *orig_mode,
|
||||
struct drm_display_mode *mode,
|
||||
int x, int y,
|
||||
struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_encoder *encoder;
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
struct ipu_di_signal_cfg sig_cfg = {};
|
||||
unsigned long encoder_types = 0;
|
||||
int ret;
|
||||
|
||||
dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
|
||||
mode->hdisplay);
|
||||
dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
|
||||
mode->vdisplay);
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
|
||||
if (encoder->crtc == crtc)
|
||||
encoder_types |= BIT(encoder->encoder_type);
|
||||
|
||||
dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n",
|
||||
__func__, encoder_types);
|
||||
|
||||
/*
|
||||
* If we have DAC or LDB, then we need the IPU DI clock to be
|
||||
* the same as the LDB DI clock. For TVDAC, derive the IPU DI
|
||||
* clock from 27 MHz TVE_DI clock, but allow to divide it.
|
||||
*/
|
||||
if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) |
|
||||
BIT(DRM_MODE_ENCODER_LVDS)))
|
||||
sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT;
|
||||
else if (encoder_types & BIT(DRM_MODE_ENCODER_TVDAC))
|
||||
sig_cfg.clkflags = IPU_DI_CLKMODE_EXT;
|
||||
else
|
||||
sig_cfg.clkflags = 0;
|
||||
|
||||
sig_cfg.enable_pol = !(ipu_crtc->bus_flags & DRM_BUS_FLAG_DE_LOW);
|
||||
/* Default to driving pixel data on negative clock edges */
|
||||
sig_cfg.clk_pol = !!(ipu_crtc->bus_flags &
|
||||
DRM_BUS_FLAG_PIXDATA_POSEDGE);
|
||||
sig_cfg.bus_format = ipu_crtc->bus_format;
|
||||
sig_cfg.v_to_h_sync = 0;
|
||||
sig_cfg.hsync_pin = ipu_crtc->di_hsync_pin;
|
||||
sig_cfg.vsync_pin = ipu_crtc->di_vsync_pin;
|
||||
|
||||
drm_display_mode_to_videomode(mode, &sig_cfg.mode);
|
||||
|
||||
ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
|
||||
mode->flags & DRM_MODE_FLAG_INTERLACE,
|
||||
ipu_crtc->bus_format, mode->hdisplay);
|
||||
if (ret) {
|
||||
dev_err(ipu_crtc->dev,
|
||||
"initializing display controller failed with %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
|
||||
if (ret) {
|
||||
dev_err(ipu_crtc->dev,
|
||||
"initializing panel failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ipu_plane_mode_set(ipu_crtc->plane[0], crtc, mode,
|
||||
crtc->primary->fb,
|
||||
0, 0, mode->hdisplay, mode->vdisplay,
|
||||
x, y, mode->hdisplay, mode->vdisplay,
|
||||
mode->flags & DRM_MODE_FLAG_INTERLACE);
|
||||
}
|
||||
|
||||
static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct drm_device *drm = ipu_crtc->base.dev;
|
||||
struct ipu_flip_work *work = ipu_crtc->flip_work;
|
||||
|
||||
spin_lock_irqsave(&drm->event_lock, flags);
|
||||
if (work->page_flip_event)
|
||||
drm_crtc_send_vblank_event(&ipu_crtc->base,
|
||||
work->page_flip_event);
|
||||
imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
|
||||
spin_unlock_irqrestore(&drm->event_lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct ipu_crtc *ipu_crtc = dev_id;
|
||||
|
||||
imx_drm_handle_vblank(ipu_crtc->imx_crtc);
|
||||
|
||||
if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) {
|
||||
struct ipu_plane *plane = ipu_crtc->plane[0];
|
||||
|
||||
ipu_plane_set_base(plane, ipu_crtc->base.primary->fb,
|
||||
plane->x, plane->y);
|
||||
ipu_crtc_handle_pageflip(ipu_crtc);
|
||||
queue_work(ipu_crtc->flip_queue,
|
||||
&ipu_crtc->flip_work->unref_work);
|
||||
ipu_crtc->flip_state = IPU_FLIP_NONE;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -355,31 +153,97 @@ static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
|
|||
if (ret)
|
||||
return false;
|
||||
|
||||
if ((vm.vsync_len == 0) || (vm.hsync_len == 0))
|
||||
return false;
|
||||
|
||||
drm_display_mode_from_videomode(&vm, adjusted_mode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ipu_crtc_prepare(struct drm_crtc *crtc)
|
||||
static int ipu_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
u32 primary_plane_mask = 1 << drm_plane_index(crtc->primary);
|
||||
|
||||
ipu_fb_disable(ipu_crtc);
|
||||
if (state->active && (primary_plane_mask & state->plane_mask) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipu_crtc_commit(struct drm_crtc *crtc)
|
||||
static void ipu_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_crtc_state)
|
||||
{
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
if (crtc->state->event) {
|
||||
WARN_ON(drm_crtc_vblank_get(crtc));
|
||||
drm_crtc_arm_vblank_event(crtc, crtc->state->event);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
}
|
||||
|
||||
ipu_fb_enable(ipu_crtc);
|
||||
static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_encoder *encoder;
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
||||
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc->state);
|
||||
struct ipu_di_signal_cfg sig_cfg = {};
|
||||
unsigned long encoder_types = 0;
|
||||
|
||||
dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
|
||||
mode->hdisplay);
|
||||
dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
|
||||
mode->vdisplay);
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
if (encoder->crtc == crtc)
|
||||
encoder_types |= BIT(encoder->encoder_type);
|
||||
}
|
||||
|
||||
dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n",
|
||||
__func__, encoder_types);
|
||||
|
||||
/*
|
||||
* If we have DAC or LDB, then we need the IPU DI clock to be
|
||||
* the same as the LDB DI clock. For TVDAC, derive the IPU DI
|
||||
* clock from 27 MHz TVE_DI clock, but allow to divide it.
|
||||
*/
|
||||
if (encoder_types & (BIT(DRM_MODE_ENCODER_DAC) |
|
||||
BIT(DRM_MODE_ENCODER_LVDS)))
|
||||
sig_cfg.clkflags = IPU_DI_CLKMODE_SYNC | IPU_DI_CLKMODE_EXT;
|
||||
else if (encoder_types & BIT(DRM_MODE_ENCODER_TVDAC))
|
||||
sig_cfg.clkflags = IPU_DI_CLKMODE_EXT;
|
||||
else
|
||||
sig_cfg.clkflags = 0;
|
||||
|
||||
sig_cfg.enable_pol = !(imx_crtc_state->bus_flags & DRM_BUS_FLAG_DE_LOW);
|
||||
/* Default to driving pixel data on negative clock edges */
|
||||
sig_cfg.clk_pol = !!(imx_crtc_state->bus_flags &
|
||||
DRM_BUS_FLAG_PIXDATA_POSEDGE);
|
||||
sig_cfg.bus_format = imx_crtc_state->bus_format;
|
||||
sig_cfg.v_to_h_sync = 0;
|
||||
sig_cfg.hsync_pin = imx_crtc_state->di_hsync_pin;
|
||||
sig_cfg.vsync_pin = imx_crtc_state->di_vsync_pin;
|
||||
|
||||
drm_display_mode_to_videomode(mode, &sig_cfg.mode);
|
||||
|
||||
ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
|
||||
mode->flags & DRM_MODE_FLAG_INTERLACE,
|
||||
imx_crtc_state->bus_format, mode->hdisplay);
|
||||
ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
|
||||
}
|
||||
|
||||
static const struct drm_crtc_helper_funcs ipu_helper_funcs = {
|
||||
.dpms = ipu_crtc_dpms,
|
||||
.mode_fixup = ipu_crtc_mode_fixup,
|
||||
.mode_set = ipu_crtc_mode_set,
|
||||
.prepare = ipu_crtc_prepare,
|
||||
.commit = ipu_crtc_commit,
|
||||
.mode_set_nofb = ipu_crtc_mode_set_nofb,
|
||||
.atomic_check = ipu_crtc_atomic_check,
|
||||
.atomic_begin = ipu_crtc_atomic_begin,
|
||||
.disable = ipu_crtc_disable,
|
||||
.enable = ipu_crtc_enable,
|
||||
};
|
||||
|
||||
static int ipu_enable_vblank(struct drm_crtc *crtc)
|
||||
|
@ -398,23 +262,9 @@ static void ipu_disable_vblank(struct drm_crtc *crtc)
|
|||
disable_irq_nosync(ipu_crtc->irq);
|
||||
}
|
||||
|
||||
static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc,
|
||||
u32 bus_format, int hsync_pin, int vsync_pin, u32 bus_flags)
|
||||
{
|
||||
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
|
||||
|
||||
ipu_crtc->bus_format = bus_format;
|
||||
ipu_crtc->bus_flags = bus_flags;
|
||||
ipu_crtc->di_hsync_pin = hsync_pin;
|
||||
ipu_crtc->di_vsync_pin = vsync_pin;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = {
|
||||
.enable_vblank = ipu_enable_vblank,
|
||||
.disable_vblank = ipu_disable_vblank,
|
||||
.set_interface_pix_fmt = ipu_set_interface_pix_fmt,
|
||||
.crtc_funcs = &ipu_crtc_funcs,
|
||||
.crtc_helper_funcs = &ipu_helper_funcs,
|
||||
};
|
||||
|
@ -496,8 +346,16 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
|
|||
IPU_DP_FLOW_SYNC_FG,
|
||||
drm_crtc_mask(&ipu_crtc->base),
|
||||
DRM_PLANE_TYPE_OVERLAY);
|
||||
if (IS_ERR(ipu_crtc->plane[1]))
|
||||
if (IS_ERR(ipu_crtc->plane[1])) {
|
||||
ipu_crtc->plane[1] = NULL;
|
||||
} else {
|
||||
ret = ipu_plane_get_resources(ipu_crtc->plane[1]);
|
||||
if (ret) {
|
||||
dev_err(ipu_crtc->dev, "getting plane 1 "
|
||||
"resources failed with %d.\n", ret);
|
||||
goto err_put_plane0_res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ipu_crtc->irq = ipu_plane_irq(ipu_crtc->plane[0]);
|
||||
|
@ -505,16 +363,17 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
|
|||
"imx_drm", ipu_crtc);
|
||||
if (ret < 0) {
|
||||
dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
|
||||
goto err_put_plane_res;
|
||||
goto err_put_plane1_res;
|
||||
}
|
||||
/* Only enable IRQ when we actually need it to trigger work. */
|
||||
disable_irq(ipu_crtc->irq);
|
||||
|
||||
ipu_crtc->flip_queue = create_singlethread_workqueue("ipu-crtc-flip");
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_plane_res:
|
||||
err_put_plane1_res:
|
||||
if (ipu_crtc->plane[1])
|
||||
ipu_plane_put_resources(ipu_crtc->plane[1]);
|
||||
err_put_plane0_res:
|
||||
ipu_plane_put_resources(ipu_crtc->plane[0]);
|
||||
err_remove_crtc:
|
||||
imx_drm_remove_crtc(ipu_crtc->imx_crtc);
|
||||
|
@ -553,9 +412,10 @@ static void ipu_drm_unbind(struct device *dev, struct device *master,
|
|||
|
||||
imx_drm_remove_crtc(ipu_crtc->imx_crtc);
|
||||
|
||||
destroy_workqueue(ipu_crtc->flip_queue);
|
||||
ipu_plane_put_resources(ipu_crtc->plane[0]);
|
||||
ipu_put_resources(ipu_crtc);
|
||||
if (ipu_crtc->plane[1])
|
||||
ipu_plane_put_resources(ipu_crtc->plane[1]);
|
||||
ipu_plane_put_resources(ipu_crtc->plane[0]);
|
||||
}
|
||||
|
||||
static const struct component_ops ipu_crtc_ops = {
|
||||
|
|
|
@ -14,13 +14,19 @@
|
|||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
|
||||
#include "video/imx-ipu-v3.h"
|
||||
#include "ipuv3-plane.h"
|
||||
|
||||
#define to_ipu_plane(x) container_of(x, struct ipu_plane, base)
|
||||
static inline struct ipu_plane *to_ipu_plane(struct drm_plane *p)
|
||||
{
|
||||
return container_of(p, struct ipu_plane, base);
|
||||
}
|
||||
|
||||
static const uint32_t ipu_plane_formats[] = {
|
||||
DRM_FORMAT_ARGB1555,
|
||||
|
@ -53,62 +59,67 @@ int ipu_plane_irq(struct ipu_plane *ipu_plane)
|
|||
IPU_IRQ_EOF);
|
||||
}
|
||||
|
||||
static int calc_vref(struct drm_display_mode *mode)
|
||||
static inline unsigned long
|
||||
drm_plane_state_to_eba(struct drm_plane_state *state)
|
||||
{
|
||||
unsigned long htotal, vtotal;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
|
||||
htotal = mode->htotal;
|
||||
vtotal = mode->vtotal;
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
|
||||
BUG_ON(!cma_obj);
|
||||
|
||||
if (!htotal || !vtotal)
|
||||
return 60;
|
||||
|
||||
return DIV_ROUND_UP(mode->clock * 1000, vtotal * htotal);
|
||||
return cma_obj->paddr + fb->offsets[0] +
|
||||
fb->pitches[0] * (state->src_y >> 16) +
|
||||
(fb->bits_per_pixel >> 3) * (state->src_x >> 16);
|
||||
}
|
||||
|
||||
static inline int calc_bandwidth(int width, int height, unsigned int vref)
|
||||
static inline unsigned long
|
||||
drm_plane_state_to_ubo(struct drm_plane_state *state)
|
||||
{
|
||||
return width * height * vref;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
unsigned long eba = drm_plane_state_to_eba(state);
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 1);
|
||||
BUG_ON(!cma_obj);
|
||||
|
||||
return cma_obj->paddr + fb->offsets[1] +
|
||||
fb->pitches[1] * (state->src_y >> 16) / 2 +
|
||||
(state->src_x >> 16) / 2 - eba;
|
||||
}
|
||||
|
||||
int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
|
||||
int x, int y)
|
||||
static inline unsigned long
|
||||
drm_plane_state_to_vbo(struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_gem_cma_object *cma_obj[3];
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
unsigned long eba = drm_plane_state_to_eba(state);
|
||||
|
||||
cma_obj = drm_fb_cma_get_gem_obj(fb, 2);
|
||||
BUG_ON(!cma_obj);
|
||||
|
||||
return cma_obj->paddr + fb->offsets[2] +
|
||||
fb->pitches[2] * (state->src_y >> 16) / 2 +
|
||||
(state->src_x >> 16) / 2 - eba;
|
||||
}
|
||||
|
||||
static void ipu_plane_atomic_set_base(struct ipu_plane *ipu_plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct drm_plane *plane = &ipu_plane->base;
|
||||
struct drm_plane_state *state = plane->state;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
unsigned long eba, ubo, vbo;
|
||||
int active, i;
|
||||
int active;
|
||||
|
||||
for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) {
|
||||
cma_obj[i] = drm_fb_cma_get_gem_obj(fb, i);
|
||||
if (!cma_obj[i]) {
|
||||
DRM_DEBUG_KMS("plane %d entry is null.\n", i);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
eba = cma_obj[0]->paddr + fb->offsets[0] +
|
||||
fb->pitches[0] * y + (fb->bits_per_pixel >> 3) * x;
|
||||
|
||||
if (eba & 0x7) {
|
||||
DRM_DEBUG_KMS("base address must be a multiple of 8.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fb->pitches[0] < 1 || fb->pitches[0] > 16384) {
|
||||
DRM_DEBUG_KMS("pitches out of range.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ipu_plane->enabled && fb->pitches[0] != ipu_plane->stride[0]) {
|
||||
DRM_DEBUG_KMS("pitches must not change while plane is enabled.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ipu_plane->stride[0] = fb->pitches[0];
|
||||
eba = drm_plane_state_to_eba(state);
|
||||
|
||||
switch (fb->pixel_format) {
|
||||
case DRM_FORMAT_YUV420:
|
||||
case DRM_FORMAT_YVU420:
|
||||
if (old_state->fb)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Multiplanar formats have to meet the following restrictions:
|
||||
* - The (up to) three plane addresses are EBA, EBA+UBO, EBA+VBO
|
||||
|
@ -117,59 +128,28 @@ int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
|
|||
* - Only EBA may be changed while scanout is active
|
||||
* - The strides of U and V planes must be identical.
|
||||
*/
|
||||
ubo = cma_obj[1]->paddr + fb->offsets[1] +
|
||||
fb->pitches[1] * y / 2 + x / 2 - eba;
|
||||
vbo = cma_obj[2]->paddr + fb->offsets[2] +
|
||||
fb->pitches[2] * y / 2 + x / 2 - eba;
|
||||
ubo = drm_plane_state_to_ubo(state);
|
||||
vbo = drm_plane_state_to_vbo(state);
|
||||
|
||||
if ((ubo & 0x7) || (vbo & 0x7)) {
|
||||
DRM_DEBUG_KMS("U/V buffer offsets must be a multiple of 8.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((ubo > 0xfffff8) || (vbo > 0xfffff8)) {
|
||||
DRM_DEBUG_KMS("U/V buffer offsets must be positive and not larger than 0xfffff8.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ipu_plane->enabled && ((ipu_plane->u_offset != ubo) ||
|
||||
(ipu_plane->v_offset != vbo))) {
|
||||
DRM_DEBUG_KMS("U/V buffer offsets must not change while plane is enabled.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fb->pitches[1] != fb->pitches[2]) {
|
||||
DRM_DEBUG_KMS("U/V pitches must be identical.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fb->pitches[1] < 1 || fb->pitches[1] > 16384) {
|
||||
DRM_DEBUG_KMS("U/V pitches out of range.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ipu_plane->enabled &&
|
||||
(ipu_plane->stride[1] != fb->pitches[1])) {
|
||||
DRM_DEBUG_KMS("U/V pitches must not change while plane is enabled.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ipu_plane->u_offset = ubo;
|
||||
ipu_plane->v_offset = vbo;
|
||||
ipu_plane->stride[1] = fb->pitches[1];
|
||||
if (fb->pixel_format == DRM_FORMAT_YUV420)
|
||||
ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
|
||||
fb->pitches[1], ubo, vbo);
|
||||
else
|
||||
ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
|
||||
fb->pitches[1], vbo, ubo);
|
||||
|
||||
dev_dbg(ipu_plane->base.dev->dev,
|
||||
"phys = %pad %pad %pad, x = %d, y = %d",
|
||||
&cma_obj[0]->paddr, &cma_obj[1]->paddr,
|
||||
&cma_obj[2]->paddr, x, y);
|
||||
"phy = %lu %lu %lu, x = %d, y = %d", eba, ubo, vbo,
|
||||
state->src_x >> 16, state->src_y >> 16);
|
||||
break;
|
||||
default:
|
||||
dev_dbg(ipu_plane->base.dev->dev, "phys = %pad, x = %d, y = %d",
|
||||
&cma_obj[0]->paddr, x, y);
|
||||
dev_dbg(ipu_plane->base.dev->dev, "phys = %lu, x = %d, y = %d",
|
||||
eba, state->src_x >> 16, state->src_y >> 16);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (ipu_plane->enabled) {
|
||||
if (old_state->fb) {
|
||||
active = ipu_idmac_get_current_buffer(ipu_plane->ipu_ch);
|
||||
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, !active, eba);
|
||||
ipu_idmac_select_buffer(ipu_plane->ipu_ch, !active);
|
||||
|
@ -177,155 +157,6 @@ int ipu_plane_set_base(struct ipu_plane *ipu_plane, struct drm_framebuffer *fb,
|
|||
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 0, eba);
|
||||
ipu_cpmem_set_buffer(ipu_plane->ipu_ch, 1, eba);
|
||||
}
|
||||
|
||||
/* cache offsets for subsequent pageflips */
|
||||
ipu_plane->x = x;
|
||||
ipu_plane->y = y;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipu_plane_mode_set(struct ipu_plane *ipu_plane, struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h, bool interlaced)
|
||||
{
|
||||
struct device *dev = ipu_plane->base.dev->dev;
|
||||
int ret;
|
||||
|
||||
/* no scaling */
|
||||
if (src_w != crtc_w || src_h != crtc_h)
|
||||
return -EINVAL;
|
||||
|
||||
/* clip to crtc bounds */
|
||||
if (crtc_x < 0) {
|
||||
if (-crtc_x > crtc_w)
|
||||
return -EINVAL;
|
||||
src_x += -crtc_x;
|
||||
src_w -= -crtc_x;
|
||||
crtc_w -= -crtc_x;
|
||||
crtc_x = 0;
|
||||
}
|
||||
if (crtc_y < 0) {
|
||||
if (-crtc_y > crtc_h)
|
||||
return -EINVAL;
|
||||
src_y += -crtc_y;
|
||||
src_h -= -crtc_y;
|
||||
crtc_h -= -crtc_y;
|
||||
crtc_y = 0;
|
||||
}
|
||||
if (crtc_x + crtc_w > mode->hdisplay) {
|
||||
if (crtc_x > mode->hdisplay)
|
||||
return -EINVAL;
|
||||
crtc_w = mode->hdisplay - crtc_x;
|
||||
src_w = crtc_w;
|
||||
}
|
||||
if (crtc_y + crtc_h > mode->vdisplay) {
|
||||
if (crtc_y > mode->vdisplay)
|
||||
return -EINVAL;
|
||||
crtc_h = mode->vdisplay - crtc_y;
|
||||
src_h = crtc_h;
|
||||
}
|
||||
/* full plane minimum width is 13 pixels */
|
||||
if (crtc_w < 13 && (ipu_plane->dp_flow != IPU_DP_FLOW_SYNC_FG))
|
||||
return -EINVAL;
|
||||
if (crtc_h < 2)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* since we cannot touch active IDMAC channels, we do not support
|
||||
* resizing the enabled plane or changing its format
|
||||
*/
|
||||
if (ipu_plane->enabled) {
|
||||
if (src_w != ipu_plane->w || src_h != ipu_plane->h ||
|
||||
fb->pixel_format != ipu_plane->base.fb->pixel_format)
|
||||
return -EINVAL;
|
||||
|
||||
return ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
|
||||
}
|
||||
|
||||
switch (ipu_plane->dp_flow) {
|
||||
case IPU_DP_FLOW_SYNC_BG:
|
||||
ret = ipu_dp_setup_channel(ipu_plane->dp,
|
||||
IPUV3_COLORSPACE_RGB,
|
||||
IPUV3_COLORSPACE_RGB);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"initializing display processor failed with %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
|
||||
break;
|
||||
case IPU_DP_FLOW_SYNC_FG:
|
||||
ipu_dp_setup_channel(ipu_plane->dp,
|
||||
ipu_drm_fourcc_to_colorspace(fb->pixel_format),
|
||||
IPUV3_COLORSPACE_UNKNOWN);
|
||||
ipu_dp_set_window_pos(ipu_plane->dp, crtc_x, crtc_y);
|
||||
/* Enable local alpha on partial plane */
|
||||
switch (fb->pixel_format) {
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_ABGR1555:
|
||||
case DRM_FORMAT_RGBA5551:
|
||||
case DRM_FORMAT_BGRA5551:
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ipu_dmfc_alloc_bandwidth(ipu_plane->dmfc,
|
||||
calc_bandwidth(crtc_w, crtc_h,
|
||||
calc_vref(mode)), 64);
|
||||
if (ret) {
|
||||
dev_err(dev, "allocating dmfc bandwidth failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ipu_dmfc_config_wait4eot(ipu_plane->dmfc, crtc_w);
|
||||
|
||||
ipu_cpmem_zero(ipu_plane->ipu_ch);
|
||||
ipu_cpmem_set_resolution(ipu_plane->ipu_ch, src_w, src_h);
|
||||
ret = ipu_cpmem_set_fmt(ipu_plane->ipu_ch, fb->pixel_format);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "unsupported pixel format 0x%08x\n",
|
||||
fb->pixel_format);
|
||||
return ret;
|
||||
}
|
||||
ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
|
||||
ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
|
||||
ipu_cpmem_set_stride(ipu_plane->ipu_ch, fb->pitches[0]);
|
||||
|
||||
ret = ipu_plane_set_base(ipu_plane, fb, src_x, src_y);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (interlaced)
|
||||
ipu_cpmem_interlaced_scan(ipu_plane->ipu_ch, fb->pitches[0]);
|
||||
|
||||
if (fb->pixel_format == DRM_FORMAT_YUV420) {
|
||||
ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
|
||||
ipu_plane->stride[1],
|
||||
ipu_plane->u_offset,
|
||||
ipu_plane->v_offset);
|
||||
} else if (fb->pixel_format == DRM_FORMAT_YVU420) {
|
||||
ipu_cpmem_set_yuv_planar_full(ipu_plane->ipu_ch,
|
||||
ipu_plane->stride[1],
|
||||
ipu_plane->v_offset,
|
||||
ipu_plane->u_offset);
|
||||
}
|
||||
|
||||
ipu_plane->w = src_w;
|
||||
ipu_plane->h = src_h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipu_plane_put_resources(struct ipu_plane *ipu_plane)
|
||||
|
@ -372,7 +203,7 @@ int ipu_plane_get_resources(struct ipu_plane *ipu_plane)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void ipu_plane_enable(struct ipu_plane *ipu_plane)
|
||||
static void ipu_plane_enable(struct ipu_plane *ipu_plane)
|
||||
{
|
||||
if (ipu_plane->dp)
|
||||
ipu_dp_enable(ipu_plane->ipu);
|
||||
|
@ -380,14 +211,10 @@ void ipu_plane_enable(struct ipu_plane *ipu_plane)
|
|||
ipu_idmac_enable_channel(ipu_plane->ipu_ch);
|
||||
if (ipu_plane->dp)
|
||||
ipu_dp_enable_channel(ipu_plane->dp);
|
||||
|
||||
ipu_plane->enabled = true;
|
||||
}
|
||||
|
||||
void ipu_plane_disable(struct ipu_plane *ipu_plane)
|
||||
static void ipu_plane_disable(struct ipu_plane *ipu_plane)
|
||||
{
|
||||
ipu_plane->enabled = false;
|
||||
|
||||
ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
|
||||
|
||||
if (ipu_plane->dp)
|
||||
|
@ -398,55 +225,13 @@ void ipu_plane_disable(struct ipu_plane *ipu_plane)
|
|||
ipu_dp_disable(ipu_plane->ipu);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_plane API
|
||||
*/
|
||||
|
||||
static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
int ret = 0;
|
||||
|
||||
DRM_DEBUG_KMS("plane - %p\n", plane);
|
||||
|
||||
if (!ipu_plane->enabled)
|
||||
ret = ipu_plane_get_resources(ipu_plane);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ipu_plane_mode_set(ipu_plane, crtc, &crtc->hwmode, fb,
|
||||
crtc_x, crtc_y, crtc_w, crtc_h,
|
||||
src_x >> 16, src_y >> 16, src_w >> 16, src_h >> 16,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
ipu_plane_put_resources(ipu_plane);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (crtc != plane->crtc)
|
||||
dev_dbg(plane->dev->dev, "crtc change: %p -> %p\n",
|
||||
plane->crtc, crtc);
|
||||
|
||||
if (!ipu_plane->enabled)
|
||||
ipu_plane_enable(ipu_plane);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipu_disable_plane(struct drm_plane *plane)
|
||||
{
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
|
||||
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
||||
|
||||
if (ipu_plane->enabled)
|
||||
ipu_plane_disable(ipu_plane);
|
||||
|
||||
ipu_plane_put_resources(ipu_plane);
|
||||
ipu_plane_disable(ipu_plane);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -463,9 +248,202 @@ static void ipu_plane_destroy(struct drm_plane *plane)
|
|||
}
|
||||
|
||||
static const struct drm_plane_funcs ipu_plane_funcs = {
|
||||
.update_plane = ipu_update_plane,
|
||||
.disable_plane = ipu_disable_plane,
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = ipu_plane_destroy,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
};
|
||||
|
||||
static int ipu_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct drm_plane_state *old_state = plane->state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct device *dev = plane->dev->dev;
|
||||
struct drm_framebuffer *fb = state->fb;
|
||||
struct drm_framebuffer *old_fb = old_state->fb;
|
||||
unsigned long eba, ubo, vbo, old_ubo, old_vbo;
|
||||
|
||||
/* Ok to disable */
|
||||
if (!fb)
|
||||
return 0;
|
||||
|
||||
if (!state->crtc)
|
||||
return -EINVAL;
|
||||
|
||||
crtc_state =
|
||||
drm_atomic_get_existing_crtc_state(state->state, state->crtc);
|
||||
if (WARN_ON(!crtc_state))
|
||||
return -EINVAL;
|
||||
|
||||
/* CRTC should be enabled */
|
||||
if (!crtc_state->enable)
|
||||
return -EINVAL;
|
||||
|
||||
/* no scaling */
|
||||
if (state->src_w >> 16 != state->crtc_w ||
|
||||
state->src_h >> 16 != state->crtc_h)
|
||||
return -EINVAL;
|
||||
|
||||
switch (plane->type) {
|
||||
case DRM_PLANE_TYPE_PRIMARY:
|
||||
/* full plane doesn't support partial off screen */
|
||||
if (state->crtc_x || state->crtc_y ||
|
||||
state->crtc_w != crtc_state->adjusted_mode.hdisplay ||
|
||||
state->crtc_h != crtc_state->adjusted_mode.vdisplay)
|
||||
return -EINVAL;
|
||||
|
||||
/* full plane minimum width is 13 pixels */
|
||||
if (state->crtc_w < 13)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case DRM_PLANE_TYPE_OVERLAY:
|
||||
if (state->crtc_x < 0 || state->crtc_y < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (state->crtc_x + state->crtc_w >
|
||||
crtc_state->adjusted_mode.hdisplay)
|
||||
return -EINVAL;
|
||||
if (state->crtc_y + state->crtc_h >
|
||||
crtc_state->adjusted_mode.vdisplay)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev, "Unsupported plane type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (state->crtc_h < 2)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* since we cannot touch active IDMAC channels, we do not support
|
||||
* resizing the enabled plane or changing its format
|
||||
*/
|
||||
if (old_fb && (state->src_w != old_state->src_w ||
|
||||
state->src_h != old_state->src_h ||
|
||||
fb->pixel_format != old_fb->pixel_format))
|
||||
return -EINVAL;
|
||||
|
||||
eba = drm_plane_state_to_eba(state);
|
||||
|
||||
if (eba & 0x7)
|
||||
return -EINVAL;
|
||||
|
||||
if (fb->pitches[0] < 1 || fb->pitches[0] > 16384)
|
||||
return -EINVAL;
|
||||
|
||||
if (old_fb && fb->pitches[0] != old_fb->pitches[0])
|
||||
return -EINVAL;
|
||||
|
||||
switch (fb->pixel_format) {
|
||||
case DRM_FORMAT_YUV420:
|
||||
case DRM_FORMAT_YVU420:
|
||||
/*
|
||||
* Multiplanar formats have to meet the following restrictions:
|
||||
* - The (up to) three plane addresses are EBA, EBA+UBO, EBA+VBO
|
||||
* - EBA, UBO and VBO are a multiple of 8
|
||||
* - UBO and VBO are unsigned and not larger than 0xfffff8
|
||||
* - Only EBA may be changed while scanout is active
|
||||
* - The strides of U and V planes must be identical.
|
||||
*/
|
||||
ubo = drm_plane_state_to_ubo(state);
|
||||
vbo = drm_plane_state_to_vbo(state);
|
||||
|
||||
if ((ubo & 0x7) || (vbo & 0x7))
|
||||
return -EINVAL;
|
||||
|
||||
if ((ubo > 0xfffff8) || (vbo > 0xfffff8))
|
||||
return -EINVAL;
|
||||
|
||||
if (old_fb) {
|
||||
old_ubo = drm_plane_state_to_ubo(old_state);
|
||||
old_vbo = drm_plane_state_to_vbo(old_state);
|
||||
if (ubo != old_ubo || vbo != old_vbo)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fb->pitches[1] != fb->pitches[2])
|
||||
return -EINVAL;
|
||||
|
||||
if (fb->pitches[1] < 1 || fb->pitches[1] > 16384)
|
||||
return -EINVAL;
|
||||
|
||||
if (old_fb && old_fb->pitches[1] != fb->pitches[1])
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipu_plane_atomic_disable(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
ipu_disable_plane(plane);
|
||||
}
|
||||
|
||||
static void ipu_plane_atomic_update(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state)
|
||||
{
|
||||
struct ipu_plane *ipu_plane = to_ipu_plane(plane);
|
||||
struct drm_plane_state *state = plane->state;
|
||||
enum ipu_color_space ics;
|
||||
|
||||
if (old_state->fb) {
|
||||
ipu_plane_atomic_set_base(ipu_plane, old_state);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ipu_plane->dp_flow) {
|
||||
case IPU_DP_FLOW_SYNC_BG:
|
||||
ipu_dp_setup_channel(ipu_plane->dp,
|
||||
IPUV3_COLORSPACE_RGB,
|
||||
IPUV3_COLORSPACE_RGB);
|
||||
ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true);
|
||||
break;
|
||||
case IPU_DP_FLOW_SYNC_FG:
|
||||
ics = ipu_drm_fourcc_to_colorspace(state->fb->pixel_format);
|
||||
ipu_dp_setup_channel(ipu_plane->dp, ics,
|
||||
IPUV3_COLORSPACE_UNKNOWN);
|
||||
ipu_dp_set_window_pos(ipu_plane->dp, state->crtc_x,
|
||||
state->crtc_y);
|
||||
/* Enable local alpha on partial plane */
|
||||
switch (state->fb->pixel_format) {
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_ABGR1555:
|
||||
case DRM_FORMAT_RGBA5551:
|
||||
case DRM_FORMAT_BGRA5551:
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
ipu_dp_set_global_alpha(ipu_plane->dp, false, 0, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ipu_dmfc_config_wait4eot(ipu_plane->dmfc, state->crtc_w);
|
||||
|
||||
ipu_cpmem_zero(ipu_plane->ipu_ch);
|
||||
ipu_cpmem_set_resolution(ipu_plane->ipu_ch, state->src_w >> 16,
|
||||
state->src_h >> 16);
|
||||
ipu_cpmem_set_fmt(ipu_plane->ipu_ch, state->fb->pixel_format);
|
||||
ipu_cpmem_set_high_priority(ipu_plane->ipu_ch);
|
||||
ipu_idmac_set_double_buffer(ipu_plane->ipu_ch, 1);
|
||||
ipu_cpmem_set_stride(ipu_plane->ipu_ch, state->fb->pitches[0]);
|
||||
ipu_plane_atomic_set_base(ipu_plane, old_state);
|
||||
ipu_plane_enable(ipu_plane);
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs ipu_plane_helper_funcs = {
|
||||
.atomic_check = ipu_plane_atomic_check,
|
||||
.atomic_disable = ipu_plane_atomic_disable,
|
||||
.atomic_update = ipu_plane_atomic_update,
|
||||
};
|
||||
|
||||
struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
|
||||
|
@ -498,5 +476,7 @@ struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_plane_helper_add(&ipu_plane->base, &ipu_plane_helper_funcs);
|
||||
|
||||
return ipu_plane;
|
||||
}
|
||||
|
|
|
@ -23,17 +23,6 @@ struct ipu_plane {
|
|||
|
||||
int dma;
|
||||
int dp_flow;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
|
||||
unsigned int u_offset;
|
||||
unsigned int v_offset;
|
||||
unsigned int stride[2];
|
||||
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct ipu_plane *ipu_plane_init(struct drm_device *dev, struct ipu_soc *ipu,
|
||||
|
@ -48,11 +37,6 @@ int ipu_plane_mode_set(struct ipu_plane *plane, struct drm_crtc *crtc,
|
|||
uint32_t src_x, uint32_t src_y, uint32_t src_w,
|
||||
uint32_t src_h, bool interlaced);
|
||||
|
||||
void ipu_plane_enable(struct ipu_plane *plane);
|
||||
void ipu_plane_disable(struct ipu_plane *plane);
|
||||
int ipu_plane_set_base(struct ipu_plane *plane, struct drm_framebuffer *fb,
|
||||
int x, int y);
|
||||
|
||||
int ipu_plane_get_resources(struct ipu_plane *plane);
|
||||
void ipu_plane_put_resources(struct ipu_plane *plane);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/component.h>
|
||||
#include <linux/module.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_panel.h>
|
||||
|
@ -25,9 +26,6 @@
|
|||
|
||||
#include "imx-drm.h"
|
||||
|
||||
#define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector)
|
||||
#define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder)
|
||||
|
||||
struct imx_parallel_display {
|
||||
struct drm_connector connector;
|
||||
struct drm_encoder encoder;
|
||||
|
@ -37,8 +35,19 @@ struct imx_parallel_display {
|
|||
u32 bus_format;
|
||||
struct drm_display_mode mode;
|
||||
struct drm_panel *panel;
|
||||
struct drm_bridge *bridge;
|
||||
};
|
||||
|
||||
static inline struct imx_parallel_display *con_to_imxpd(struct drm_connector *c)
|
||||
{
|
||||
return container_of(c, struct imx_parallel_display, connector);
|
||||
}
|
||||
|
||||
static inline struct imx_parallel_display *enc_to_imxpd(struct drm_encoder *e)
|
||||
{
|
||||
return container_of(e, struct imx_parallel_display, encoder);
|
||||
}
|
||||
|
||||
static enum drm_connector_status imx_pd_connector_detect(
|
||||
struct drm_connector *connector, bool force)
|
||||
{
|
||||
|
@ -53,11 +62,7 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector)
|
|||
|
||||
if (imxpd->panel && imxpd->panel->funcs &&
|
||||
imxpd->panel->funcs->get_modes) {
|
||||
struct drm_display_info *di = &connector->display_info;
|
||||
|
||||
num_modes = imxpd->panel->funcs->get_modes(imxpd->panel);
|
||||
if (!imxpd->bus_format && di->num_bus_formats)
|
||||
imxpd->bus_format = di->bus_formats[0];
|
||||
if (num_modes > 0)
|
||||
return num_modes;
|
||||
}
|
||||
|
@ -69,10 +74,16 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector)
|
|||
|
||||
if (np) {
|
||||
struct drm_display_mode *mode = drm_mode_create(connector->dev);
|
||||
int ret;
|
||||
|
||||
if (!mode)
|
||||
return -EINVAL;
|
||||
of_get_drm_display_mode(np, &imxpd->mode, OF_USE_NATIVE_MODE);
|
||||
|
||||
ret = of_get_drm_display_mode(np, &imxpd->mode,
|
||||
OF_USE_NATIVE_MODE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_mode_copy(mode, &imxpd->mode);
|
||||
mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
|
||||
drm_mode_probed_add(connector, mode);
|
||||
|
@ -90,24 +101,7 @@ static struct drm_encoder *imx_pd_connector_best_encoder(
|
|||
return &imxpd->encoder;
|
||||
}
|
||||
|
||||
static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
drm_panel_disable(imxpd->panel);
|
||||
else
|
||||
drm_panel_enable(imxpd->panel);
|
||||
}
|
||||
|
||||
static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
|
||||
imx_drm_set_bus_config(encoder, imxpd->bus_format, 2, 3,
|
||||
imxpd->connector.display_info.bus_flags);
|
||||
}
|
||||
|
||||
static void imx_pd_encoder_commit(struct drm_encoder *encoder)
|
||||
static void imx_pd_encoder_enable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
|
||||
|
||||
|
@ -115,12 +109,6 @@ static void imx_pd_encoder_commit(struct drm_encoder *encoder)
|
|||
drm_panel_enable(imxpd->panel);
|
||||
}
|
||||
|
||||
static void imx_pd_encoder_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *orig_mode,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
}
|
||||
|
||||
static void imx_pd_encoder_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
|
||||
|
@ -129,11 +117,33 @@ static void imx_pd_encoder_disable(struct drm_encoder *encoder)
|
|||
drm_panel_unprepare(imxpd->panel);
|
||||
}
|
||||
|
||||
static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
|
||||
struct drm_display_info *di = &conn_state->connector->display_info;
|
||||
struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
|
||||
|
||||
imx_crtc_state->bus_flags = di->bus_flags;
|
||||
if (!imxpd->bus_format && di->num_bus_formats)
|
||||
imx_crtc_state->bus_format = di->bus_formats[0];
|
||||
else
|
||||
imx_crtc_state->bus_format = imxpd->bus_format;
|
||||
imx_crtc_state->di_hsync_pin = 2;
|
||||
imx_crtc_state->di_vsync_pin = 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs imx_pd_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = drm_atomic_helper_connector_dpms,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.detect = imx_pd_connector_detect,
|
||||
.destroy = imx_drm_connector_destroy,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
|
||||
|
@ -146,20 +156,18 @@ static const struct drm_encoder_funcs imx_pd_encoder_funcs = {
|
|||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
|
||||
.dpms = imx_pd_encoder_dpms,
|
||||
.prepare = imx_pd_encoder_prepare,
|
||||
.commit = imx_pd_encoder_commit,
|
||||
.mode_set = imx_pd_encoder_mode_set,
|
||||
.enable = imx_pd_encoder_enable,
|
||||
.disable = imx_pd_encoder_disable,
|
||||
.atomic_check = imx_pd_encoder_atomic_check,
|
||||
};
|
||||
|
||||
static int imx_pd_register(struct drm_device *drm,
|
||||
struct imx_parallel_display *imxpd)
|
||||
{
|
||||
struct drm_encoder *encoder = &imxpd->encoder;
|
||||
int ret;
|
||||
|
||||
ret = imx_drm_encoder_parse_of(drm, &imxpd->encoder,
|
||||
imxpd->dev->of_node);
|
||||
ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -170,19 +178,33 @@ static int imx_pd_register(struct drm_device *drm,
|
|||
*/
|
||||
imxpd->connector.dpms = DRM_MODE_DPMS_OFF;
|
||||
|
||||
drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs);
|
||||
drm_encoder_init(drm, &imxpd->encoder, &imx_pd_encoder_funcs,
|
||||
drm_encoder_helper_add(encoder, &imx_pd_encoder_helper_funcs);
|
||||
drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs,
|
||||
DRM_MODE_ENCODER_NONE, NULL);
|
||||
|
||||
drm_connector_helper_add(&imxpd->connector,
|
||||
&imx_pd_connector_helper_funcs);
|
||||
drm_connector_init(drm, &imxpd->connector, &imx_pd_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VGA);
|
||||
if (!imxpd->bridge) {
|
||||
drm_connector_helper_add(&imxpd->connector,
|
||||
&imx_pd_connector_helper_funcs);
|
||||
drm_connector_init(drm, &imxpd->connector,
|
||||
&imx_pd_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_VGA);
|
||||
}
|
||||
|
||||
if (imxpd->panel)
|
||||
drm_panel_attach(imxpd->panel, &imxpd->connector);
|
||||
|
||||
drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder);
|
||||
if (imxpd->bridge) {
|
||||
imxpd->bridge->encoder = encoder;
|
||||
encoder->bridge = imxpd->bridge;
|
||||
ret = drm_bridge_attach(drm, imxpd->bridge);
|
||||
if (ret < 0) {
|
||||
dev_err(imxpd->dev, "failed to attach bridge: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
drm_mode_connector_attach_encoder(&imxpd->connector, encoder);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -195,6 +217,7 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
|
|||
const u8 *edidp;
|
||||
struct imx_parallel_display *imxpd;
|
||||
int ret;
|
||||
u32 bus_format = 0;
|
||||
const char *fmt;
|
||||
|
||||
imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);
|
||||
|
@ -208,14 +231,15 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
|
|||
ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
|
||||
if (!ret) {
|
||||
if (!strcmp(fmt, "rgb24"))
|
||||
imxpd->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
bus_format = MEDIA_BUS_FMT_RGB888_1X24;
|
||||
else if (!strcmp(fmt, "rgb565"))
|
||||
imxpd->bus_format = MEDIA_BUS_FMT_RGB565_1X16;
|
||||
bus_format = MEDIA_BUS_FMT_RGB565_1X16;
|
||||
else if (!strcmp(fmt, "bgr666"))
|
||||
imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
|
||||
bus_format = MEDIA_BUS_FMT_RGB666_1X18;
|
||||
else if (!strcmp(fmt, "lvds666"))
|
||||
imxpd->bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
|
||||
bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
|
||||
}
|
||||
imxpd->bus_format = bus_format;
|
||||
|
||||
/* port@1 is the output port */
|
||||
ep = of_graph_get_endpoint_by_regs(np, 1, -1);
|
||||
|
@ -223,13 +247,30 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
|
|||
struct device_node *remote;
|
||||
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
of_node_put(ep);
|
||||
if (remote) {
|
||||
imxpd->panel = of_drm_find_panel(remote);
|
||||
of_node_put(remote);
|
||||
if (!remote) {
|
||||
dev_warn(dev, "endpoint %s not connected\n",
|
||||
ep->full_name);
|
||||
of_node_put(ep);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!imxpd->panel)
|
||||
of_node_put(ep);
|
||||
|
||||
imxpd->panel = of_drm_find_panel(remote);
|
||||
if (imxpd->panel) {
|
||||
dev_dbg(dev, "found panel %s\n", remote->full_name);
|
||||
} else {
|
||||
imxpd->bridge = of_drm_find_bridge(remote);
|
||||
if (imxpd->bridge)
|
||||
dev_dbg(dev, "found bridge %s\n",
|
||||
remote->full_name);
|
||||
}
|
||||
if (!imxpd->panel && !imxpd->bridge) {
|
||||
dev_dbg(dev, "waiting for panel or bridge %s\n",
|
||||
remote->full_name);
|
||||
of_node_put(remote);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
of_node_put(remote);
|
||||
}
|
||||
|
||||
imxpd->dev = dev;
|
||||
|
|
|
@ -150,6 +150,9 @@ static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand,
|
|||
static int ipu_bus_format_to_map(u32 fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
default:
|
||||
WARN_ON(1);
|
||||
/* fall-through */
|
||||
case MEDIA_BUS_FMT_RGB888_1X24:
|
||||
return IPU_DC_MAP_RGB24;
|
||||
case MEDIA_BUS_FMT_RGB565_1X16:
|
||||
|
@ -162,8 +165,6 @@ static int ipu_bus_format_to_map(u32 fmt)
|
|||
return IPU_DC_MAP_LVDS666;
|
||||
case MEDIA_BUS_FMT_BGR888_1X24:
|
||||
return IPU_DC_MAP_BGR24;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,10 +179,6 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
|
|||
dc->di = ipu_di_get_num(di);
|
||||
|
||||
map = ipu_bus_format_to_map(bus_format);
|
||||
if (map < 0) {
|
||||
dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
|
||||
return map;
|
||||
}
|
||||
|
||||
/*
|
||||
* In interlaced mode we need more counters to create the asymmetric
|
||||
|
|
|
@ -572,9 +572,6 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
|
|||
dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
|
||||
di->id, sig->mode.hactive, sig->mode.vactive);
|
||||
|
||||
if ((sig->mode.vsync_len == 0) || (sig->mode.hsync_len == 0))
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(di->ipu->dev, "Clocks: IPU %luHz DI %luHz Needed %luHz\n",
|
||||
clk_get_rate(di->clk_ipu),
|
||||
clk_get_rate(di->clk_di),
|
||||
|
|
|
@ -45,17 +45,6 @@
|
|||
#define DMFC_DP_CHAN_6B_24 16
|
||||
#define DMFC_DP_CHAN_6F_29 24
|
||||
|
||||
#define DMFC_FIFO_SIZE_64 (3 << 3)
|
||||
#define DMFC_FIFO_SIZE_128 (2 << 3)
|
||||
#define DMFC_FIFO_SIZE_256 (1 << 3)
|
||||
#define DMFC_FIFO_SIZE_512 (0 << 3)
|
||||
|
||||
#define DMFC_SEGMENT(x) ((x & 0x7) << 0)
|
||||
#define DMFC_BURSTSIZE_128 (0 << 6)
|
||||
#define DMFC_BURSTSIZE_64 (1 << 6)
|
||||
#define DMFC_BURSTSIZE_32 (2 << 6)
|
||||
#define DMFC_BURSTSIZE_16 (3 << 6)
|
||||
|
||||
struct dmfc_channel_data {
|
||||
int ipu_channel;
|
||||
unsigned long channel_reg;
|
||||
|
@ -104,9 +93,6 @@ struct ipu_dmfc_priv;
|
|||
|
||||
struct dmfc_channel {
|
||||
unsigned slots;
|
||||
unsigned slotmask;
|
||||
unsigned segment;
|
||||
int burstsize;
|
||||
struct ipu_soc *ipu;
|
||||
struct ipu_dmfc_priv *priv;
|
||||
const struct dmfc_channel_data *data;
|
||||
|
@ -117,7 +103,6 @@ struct ipu_dmfc_priv {
|
|||
struct device *dev;
|
||||
struct dmfc_channel channels[DMFC_NUM_CHANNELS];
|
||||
struct mutex mutex;
|
||||
unsigned long bandwidth_per_slot;
|
||||
void __iomem *base;
|
||||
int use_count;
|
||||
};
|
||||
|
@ -172,184 +157,6 @@ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel);
|
||||
|
||||
static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots,
|
||||
int segment, int burstsize)
|
||||
{
|
||||
struct ipu_dmfc_priv *priv = dmfc->priv;
|
||||
u32 val, field;
|
||||
|
||||
dev_dbg(priv->dev,
|
||||
"dmfc: using %d slots starting from segment %d for IPU channel %d\n",
|
||||
slots, segment, dmfc->data->ipu_channel);
|
||||
|
||||
switch (slots) {
|
||||
case 1:
|
||||
field = DMFC_FIFO_SIZE_64;
|
||||
break;
|
||||
case 2:
|
||||
field = DMFC_FIFO_SIZE_128;
|
||||
break;
|
||||
case 4:
|
||||
field = DMFC_FIFO_SIZE_256;
|
||||
break;
|
||||
case 8:
|
||||
field = DMFC_FIFO_SIZE_512;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (burstsize) {
|
||||
case 16:
|
||||
field |= DMFC_BURSTSIZE_16;
|
||||
break;
|
||||
case 32:
|
||||
field |= DMFC_BURSTSIZE_32;
|
||||
break;
|
||||
case 64:
|
||||
field |= DMFC_BURSTSIZE_64;
|
||||
break;
|
||||
case 128:
|
||||
field |= DMFC_BURSTSIZE_128;
|
||||
break;
|
||||
}
|
||||
|
||||
field |= DMFC_SEGMENT(segment);
|
||||
|
||||
val = readl(priv->base + dmfc->data->channel_reg);
|
||||
|
||||
val &= ~(0xff << dmfc->data->shift);
|
||||
val |= field << dmfc->data->shift;
|
||||
|
||||
writel(val, priv->base + dmfc->data->channel_reg);
|
||||
|
||||
dmfc->slots = slots;
|
||||
dmfc->segment = segment;
|
||||
dmfc->burstsize = burstsize;
|
||||
dmfc->slotmask = ((1 << slots) - 1) << segment;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv,
|
||||
unsigned long bandwidth)
|
||||
{
|
||||
int slots = 1;
|
||||
|
||||
while (slots * priv->bandwidth_per_slot < bandwidth)
|
||||
slots *= 2;
|
||||
|
||||
return slots;
|
||||
}
|
||||
|
||||
static int dmfc_find_slots(struct ipu_dmfc_priv *priv, int slots)
|
||||
{
|
||||
unsigned slotmask_need, slotmask_used = 0;
|
||||
int i, segment = 0;
|
||||
|
||||
slotmask_need = (1 << slots) - 1;
|
||||
|
||||
for (i = 0; i < DMFC_NUM_CHANNELS; i++)
|
||||
slotmask_used |= priv->channels[i].slotmask;
|
||||
|
||||
while (slotmask_need <= 0xff) {
|
||||
if (!(slotmask_used & slotmask_need))
|
||||
return segment;
|
||||
|
||||
slotmask_need <<= 1;
|
||||
segment++;
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
|
||||
{
|
||||
struct ipu_dmfc_priv *priv = dmfc->priv;
|
||||
int i;
|
||||
|
||||
dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n",
|
||||
dmfc->slots, dmfc->segment);
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
|
||||
if (!dmfc->slots)
|
||||
goto out;
|
||||
|
||||
dmfc->slotmask = 0;
|
||||
dmfc->slots = 0;
|
||||
dmfc->segment = 0;
|
||||
|
||||
for (i = 0; i < DMFC_NUM_CHANNELS; i++)
|
||||
priv->channels[i].slotmask = 0;
|
||||
|
||||
for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
|
||||
if (priv->channels[i].slots > 0) {
|
||||
priv->channels[i].segment =
|
||||
dmfc_find_slots(priv, priv->channels[i].slots);
|
||||
priv->channels[i].slotmask =
|
||||
((1 << priv->channels[i].slots) - 1) <<
|
||||
priv->channels[i].segment;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
|
||||
if (priv->channels[i].slots > 0)
|
||||
ipu_dmfc_setup_channel(&priv->channels[i],
|
||||
priv->channels[i].slots,
|
||||
priv->channels[i].segment,
|
||||
priv->channels[i].burstsize);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth);
|
||||
|
||||
int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
|
||||
unsigned long bandwidth_pixel_per_second, int burstsize)
|
||||
{
|
||||
struct ipu_dmfc_priv *priv = dmfc->priv;
|
||||
int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second);
|
||||
int segment = -1, ret = 0;
|
||||
|
||||
dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
|
||||
bandwidth_pixel_per_second / 1000000,
|
||||
dmfc->data->ipu_channel);
|
||||
|
||||
ipu_dmfc_free_bandwidth(dmfc);
|
||||
|
||||
mutex_lock(&priv->mutex);
|
||||
|
||||
if (slots > 8) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* For the MEM_BG channel, first try to allocate twice the slots */
|
||||
if (dmfc->data->ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC)
|
||||
segment = dmfc_find_slots(priv, slots * 2);
|
||||
else if (slots < 2)
|
||||
/* Always allocate at least 128*4 bytes (2 slots) */
|
||||
slots = 2;
|
||||
|
||||
if (segment >= 0)
|
||||
slots *= 2;
|
||||
else
|
||||
segment = dmfc_find_slots(priv, slots);
|
||||
if (segment < 0) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize);
|
||||
|
||||
out:
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth);
|
||||
|
||||
void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width)
|
||||
{
|
||||
struct ipu_dmfc_priv *priv = dmfc->priv;
|
||||
|
@ -384,7 +191,6 @@ EXPORT_SYMBOL_GPL(ipu_dmfc_get);
|
|||
|
||||
void ipu_dmfc_put(struct dmfc_channel *dmfc)
|
||||
{
|
||||
ipu_dmfc_free_bandwidth(dmfc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_dmfc_put);
|
||||
|
||||
|
@ -412,20 +218,15 @@ int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
|
|||
priv->channels[i].priv = priv;
|
||||
priv->channels[i].ipu = ipu;
|
||||
priv->channels[i].data = &dmfcdata[i];
|
||||
|
||||
if (dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_BG_SYNC ||
|
||||
dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_FG_SYNC ||
|
||||
dmfcdata[i].ipu_channel == IPUV3_CHANNEL_MEM_DC_SYNC)
|
||||
priv->channels[i].slots = 2;
|
||||
}
|
||||
|
||||
writel(0x0, priv->base + DMFC_WR_CHAN);
|
||||
writel(0x0, priv->base + DMFC_DP_CHAN);
|
||||
|
||||
/*
|
||||
* We have a total bandwidth of clkrate * 4pixel divided
|
||||
* into 8 slots.
|
||||
*/
|
||||
priv->bandwidth_per_slot = clk_get_rate(ipu_clk) * 4 / 8;
|
||||
|
||||
dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
|
||||
priv->bandwidth_per_slot / 1000000);
|
||||
|
||||
writel(0x00000050, priv->base + DMFC_WR_CHAN);
|
||||
writel(0x00005654, priv->base + DMFC_DP_CHAN);
|
||||
writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF);
|
||||
writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF);
|
||||
writel(0x00000003, priv->base + DMFC_GENERAL1);
|
||||
|
|
|
@ -235,9 +235,6 @@ int ipu_di_init_sync_panel(struct ipu_di *, struct ipu_di_signal_cfg *sig);
|
|||
struct dmfc_channel;
|
||||
int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc);
|
||||
void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc);
|
||||
int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
|
||||
unsigned long bandwidth_mbs, int burstsize);
|
||||
void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc);
|
||||
void ipu_dmfc_config_wait4eot(struct dmfc_channel *dmfc, int width);
|
||||
struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipuv3_channel);
|
||||
void ipu_dmfc_put(struct dmfc_channel *dmfc);
|
||||
|
|
Loading…
Reference in a new issue