Commit 49f98bc4 authored by Philipp Zabel's avatar Philipp Zabel

drm/imx: store internal bus configuration in crtc state

The internal bus configuration is imx-drm specific crtc state. Store it
in imx_crtc_state and let the encoder atomic_check callbacks determine
bus_flags, bus_format and the sync pins, possibly taking into account
the mode and the connector display info.
The custom imx_drm_encoder structure can be replaced again with
drm_encoder.
Signed-off-by: default avatarPhilipp Zabel <p.zabel@pengutronix.de>
parent e41c5c24
...@@ -22,14 +22,17 @@ ...@@ -22,14 +22,17 @@
#include "imx-drm.h" #include "imx-drm.h"
#define imx_enc_to_imx_hdmi(x) container_of(x, struct imx_hdmi, imx_encoder)
struct imx_hdmi { struct imx_hdmi {
struct device *dev; struct device *dev;
struct imx_drm_encoder imx_encoder; struct drm_encoder encoder;
struct regmap *regmap; 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[] = { static const struct dw_hdmi_mpll_config imx_mpll_cfg[] = {
{ {
45250000, { 45250000, {
...@@ -113,8 +116,7 @@ static void dw_hdmi_imx_encoder_disable(struct drm_encoder *encoder) ...@@ -113,8 +116,7 @@ static void dw_hdmi_imx_encoder_disable(struct drm_encoder *encoder)
static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder) static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_hdmi *hdmi = enc_to_imx_hdmi(encoder);
struct imx_hdmi *hdmi = imx_enc_to_imx_hdmi(imx_encoder);
int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder); int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder);
regmap_update_bits(hdmi->regmap, IOMUXC_GPR3, regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
...@@ -122,9 +124,23 @@ static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder) ...@@ -122,9 +124,23 @@ static void dw_hdmi_imx_encoder_enable(struct drm_encoder *encoder)
mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT); mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
} }
static int dw_hdmi_imx_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);
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 = { static const struct drm_encoder_helper_funcs dw_hdmi_imx_encoder_helper_funcs = {
.enable = dw_hdmi_imx_encoder_enable, .enable = dw_hdmi_imx_encoder_enable,
.disable = dw_hdmi_imx_encoder_disable, .disable = dw_hdmi_imx_encoder_disable,
.atomic_check = dw_hdmi_imx_atomic_check,
}; };
static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = { static const struct drm_encoder_funcs dw_hdmi_imx_encoder_funcs = {
...@@ -205,10 +221,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master, ...@@ -205,10 +221,7 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node); match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node);
plat_data = match->data; plat_data = match->data;
hdmi->dev = &pdev->dev; hdmi->dev = &pdev->dev;
encoder = &hdmi->imx_encoder.encoder; encoder = &hdmi->encoder;
hdmi->imx_encoder.bus_format = MEDIA_BUS_FMT_RGB888_1X24;
hdmi->imx_encoder.di_hsync_pin = 2;
hdmi->imx_encoder.di_vsync_pin = 3;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) if (irq < 0)
......
...@@ -15,15 +15,18 @@ struct platform_device; ...@@ -15,15 +15,18 @@ struct platform_device;
unsigned int imx_drm_crtc_id(struct imx_drm_crtc *crtc); unsigned int imx_drm_crtc_id(struct imx_drm_crtc *crtc);
struct imx_drm_encoder { struct imx_crtc_state {
struct drm_encoder encoder; struct drm_crtc_state base;
u32 bus_format; u32 bus_format;
u32 bus_flags; u32 bus_flags;
int di_hsync_pin; int di_hsync_pin;
int di_vsync_pin; int di_vsync_pin;
}; };
#define enc_to_imx_enc(x) container_of(x, struct imx_drm_encoder, encoder) 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 { struct imx_drm_crtc_helper_funcs {
int (*enable_vblank)(struct drm_crtc *crtc); int (*enable_vblank)(struct drm_crtc *crtc);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/component.h> #include <linux/component.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
#include <drm/drm_fb_helper.h> #include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
...@@ -51,15 +52,13 @@ ...@@ -51,15 +52,13 @@
#define LDB_BGREF_RMODE_INT (1 << 15) #define LDB_BGREF_RMODE_INT (1 << 15)
#define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector) #define con_to_imx_ldb_ch(x) container_of(x, struct imx_ldb_channel, connector)
#define imx_enc_to_imx_ldb_ch(x) \
container_of(x, struct imx_ldb_channel, imx_encoder)
struct imx_ldb; struct imx_ldb;
struct imx_ldb_channel { struct imx_ldb_channel {
struct imx_ldb *ldb; struct imx_ldb *ldb;
struct drm_connector connector; struct drm_connector connector;
struct imx_drm_encoder imx_encoder; struct drm_encoder encoder;
struct drm_panel *panel; struct drm_panel *panel;
struct device_node *child; struct device_node *child;
struct i2c_adapter *ddc; struct i2c_adapter *ddc;
...@@ -68,8 +67,14 @@ struct imx_ldb_channel { ...@@ -68,8 +67,14 @@ struct imx_ldb_channel {
int edid_len; int edid_len;
struct drm_display_mode mode; struct drm_display_mode mode;
int mode_valid; int mode_valid;
u32 bus_format;
}; };
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 { struct bus_mux {
int reg; int reg;
int shift; int shift;
...@@ -94,25 +99,22 @@ static enum drm_connector_status imx_ldb_connector_detect( ...@@ -94,25 +99,22 @@ static enum drm_connector_status imx_ldb_connector_detect(
return connector_status_connected; return connector_status_connected;
} }
static void imx_ldb_bus_format_translation(struct imx_ldb_channel *imx_ldb_ch, static void imx_ldb_ch_set_bus_format(struct imx_ldb_channel *imx_ldb_ch,
u32 bus_format) u32 bus_format)
{ {
struct imx_ldb *ldb = imx_ldb_ch->ldb; struct imx_ldb *ldb = imx_ldb_ch->ldb;
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
switch (bus_format) { switch (bus_format) {
case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
imx_ldb_ch->imx_encoder.bus_format = MEDIA_BUS_FMT_RGB666_1X18;
break; break;
case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
imx_ldb_ch->imx_encoder.bus_format = MEDIA_BUS_FMT_RGB888_1X24;
if (imx_ldb_ch->chno == 0 || dual) if (imx_ldb_ch->chno == 0 || dual)
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24; ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
if (imx_ldb_ch->chno == 1 || dual) if (imx_ldb_ch->chno == 1 || dual)
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24; ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
break; break;
case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
imx_ldb_ch->imx_encoder.bus_format = MEDIA_BUS_FMT_RGB888_1X24;
if (imx_ldb_ch->chno == 0 || dual) if (imx_ldb_ch->chno == 0 || dual)
ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |
LDB_BIT_MAP_CH0_JEIDA; LDB_BIT_MAP_CH0_JEIDA;
...@@ -130,12 +132,7 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector) ...@@ -130,12 +132,7 @@ static int imx_ldb_connector_get_modes(struct drm_connector *connector)
if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs && if (imx_ldb_ch->panel && imx_ldb_ch->panel->funcs &&
imx_ldb_ch->panel->funcs->get_modes) { 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); num_modes = imx_ldb_ch->panel->funcs->get_modes(imx_ldb_ch->panel);
if (!imx_ldb_ch->imx_encoder.bus_format && di->num_bus_formats)
imx_ldb_bus_format_translation(imx_ldb_ch,
di->bus_formats[0]);
if (num_modes > 0) if (num_modes > 0)
return num_modes; return num_modes;
} }
...@@ -169,7 +166,7 @@ static struct drm_encoder *imx_ldb_connector_best_encoder( ...@@ -169,7 +166,7 @@ static struct drm_encoder *imx_ldb_connector_best_encoder(
{ {
struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector); struct imx_ldb_channel *imx_ldb_ch = con_to_imx_ldb_ch(connector);
return &imx_ldb_ch->imx_encoder.encoder; return &imx_ldb_ch->encoder;
} }
static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno, static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
...@@ -202,8 +199,7 @@ static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno, ...@@ -202,8 +199,7 @@ static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
static void imx_ldb_encoder_enable(struct drm_encoder *encoder) static void imx_ldb_encoder_enable(struct drm_encoder *encoder)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb_channel *imx_ldb_ch = imx_enc_to_imx_ldb_ch(imx_encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb; struct imx_ldb *ldb = imx_ldb_ch->ldb;
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
int mux = drm_of_encoder_active_port_id(imx_ldb_ch->child, encoder); int mux = drm_of_encoder_active_port_id(imx_ldb_ch->child, encoder);
...@@ -256,13 +252,13 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, ...@@ -256,13 +252,13 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *orig_mode, struct drm_display_mode *orig_mode,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb_channel *imx_ldb_ch = imx_enc_to_imx_ldb_ch(imx_encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb; struct imx_ldb *ldb = imx_ldb_ch->ldb;
int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
unsigned long serial_clk; unsigned long serial_clk;
unsigned long di_clk = mode->clock * 1000; unsigned long di_clk = mode->clock * 1000;
int mux = drm_of_encoder_active_port_id(imx_ldb_ch->child, encoder); 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) { if (mode->clock > 170000) {
dev_warn(ldb->dev, dev_warn(ldb->dev,
...@@ -284,24 +280,41 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, ...@@ -284,24 +280,41 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
} }
/* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */ /* 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) if (mode->flags & DRM_MODE_FLAG_NVSYNC)
ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW; ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
else if (mode->flags & DRM_MODE_FLAG_PVSYNC) else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
ldb->ldb_ctrl &= ~LDB_DI0_VS_POL_ACT_LOW; 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) if (mode->flags & DRM_MODE_FLAG_NVSYNC)
ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW; ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
else if (mode->flags & DRM_MODE_FLAG_PVSYNC) else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW; 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) static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
struct imx_ldb_channel *imx_ldb_ch = imx_enc_to_imx_ldb_ch(imx_encoder);
struct imx_ldb *ldb = imx_ldb_ch->ldb; struct imx_ldb *ldb = imx_ldb_ch->ldb;
int mux, ret; int mux, ret;
...@@ -356,6 +369,37 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder) ...@@ -356,6 +369,37 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
drm_panel_unprepare(imx_ldb_ch->panel); 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 = { static const struct drm_connector_funcs imx_ldb_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms, .dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
...@@ -379,6 +423,7 @@ static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = { ...@@ -379,6 +423,7 @@ static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
.mode_set = imx_ldb_encoder_mode_set, .mode_set = imx_ldb_encoder_mode_set,
.enable = imx_ldb_encoder_enable, .enable = imx_ldb_encoder_enable,
.disable = imx_ldb_encoder_disable, .disable = imx_ldb_encoder_disable,
.atomic_check = imx_ldb_encoder_atomic_check,
}; };
static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno) static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno)
...@@ -400,10 +445,10 @@ static int imx_ldb_register(struct drm_device *drm, ...@@ -400,10 +445,10 @@ static int imx_ldb_register(struct drm_device *drm,
struct imx_ldb_channel *imx_ldb_ch) struct imx_ldb_channel *imx_ldb_ch)
{ {
struct imx_ldb *ldb = imx_ldb_ch->ldb; struct imx_ldb *ldb = imx_ldb_ch->ldb;
struct drm_encoder *encoder = &imx_ldb_ch->encoder;
int ret; int ret;
ret = imx_drm_encoder_parse_of(drm, &imx_ldb_ch->imx_encoder.encoder, ret = imx_drm_encoder_parse_of(drm, encoder, imx_ldb_ch->child);
imx_ldb_ch->child);
if (ret) if (ret)
return ret; return ret;
...@@ -417,10 +462,9 @@ static int imx_ldb_register(struct drm_device *drm, ...@@ -417,10 +462,9 @@ static int imx_ldb_register(struct drm_device *drm,
return ret; return ret;
} }
drm_encoder_helper_add(&imx_ldb_ch->imx_encoder.encoder, drm_encoder_helper_add(encoder, &imx_ldb_encoder_helper_funcs);
&imx_ldb_encoder_helper_funcs); drm_encoder_init(drm, encoder, &imx_ldb_encoder_funcs,
drm_encoder_init(drm, &imx_ldb_ch->imx_encoder.encoder, DRM_MODE_ENCODER_LVDS, NULL);
&imx_ldb_encoder_funcs, DRM_MODE_ENCODER_LVDS, NULL);
drm_connector_helper_add(&imx_ldb_ch->connector, drm_connector_helper_add(&imx_ldb_ch->connector,
&imx_ldb_connector_helper_funcs); &imx_ldb_connector_helper_funcs);
...@@ -430,8 +474,7 @@ static int imx_ldb_register(struct drm_device *drm, ...@@ -430,8 +474,7 @@ static int imx_ldb_register(struct drm_device *drm,
if (imx_ldb_ch->panel) if (imx_ldb_ch->panel)
drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector); drm_panel_attach(imx_ldb_ch->panel, &imx_ldb_ch->connector);
drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, encoder);
&imx_ldb_ch->imx_encoder.encoder);
return 0; return 0;
} }
...@@ -648,10 +691,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) ...@@ -648,10 +691,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
bus_format); bus_format);
return bus_format; return bus_format;
} }
imx_ldb_bus_format_translation(channel, bus_format); channel->bus_format = bus_format;
channel->imx_encoder.di_hsync_pin = 2;
channel->imx_encoder.di_vsync_pin = 3;
ret = imx_ldb_register(drm, channel); ret = imx_ldb_register(drm, channel);
if (ret) if (ret)
...@@ -676,8 +716,7 @@ static void imx_ldb_unbind(struct device *dev, struct device *master, ...@@ -676,8 +716,7 @@ static void imx_ldb_unbind(struct device *dev, struct device *master,
continue; continue;
channel->connector.funcs->destroy(&channel->connector); channel->connector.funcs->destroy(&channel->connector);
channel->imx_encoder.encoder.funcs->destroy( channel->encoder.funcs->destroy(&channel->encoder);
&channel->imx_encoder.encoder);
kfree(channel->edid); kfree(channel->edid);
i2c_put_adapter(channel->ddc); i2c_put_adapter(channel->ddc);
......
...@@ -99,7 +99,6 @@ ...@@ -99,7 +99,6 @@
#define TVE_TVDAC_TEST_MODE_MASK (0x7 << 0) #define TVE_TVDAC_TEST_MODE_MASK (0x7 << 0)
#define con_to_tve(x) container_of(x, struct imx_tve, connector) #define con_to_tve(x) container_of(x, struct imx_tve, connector)
#define imx_enc_to_tve(x) container_of(x, struct imx_tve, imx_encoder)
enum { enum {
TVE_MODE_TVOUT, TVE_MODE_TVOUT,
...@@ -108,11 +107,13 @@ enum { ...@@ -108,11 +107,13 @@ enum {
struct imx_tve { struct imx_tve {
struct drm_connector connector; struct drm_connector connector;
struct imx_drm_encoder imx_encoder; struct drm_encoder encoder;
struct device *dev; struct device *dev;
spinlock_t lock; /* register lock */ spinlock_t lock; /* register lock */
bool enabled; bool enabled;
int mode; int mode;
int di_hsync_pin;
int di_vsync_pin;
struct regmap *regmap; struct regmap *regmap;
struct regulator *dac_reg; struct regulator *dac_reg;
...@@ -123,6 +124,11 @@ struct imx_tve { ...@@ -123,6 +124,11 @@ struct imx_tve {
struct clk *di_clk; struct clk *di_clk;
}; };
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) static void tve_lock(void *__tve)
__acquires(&tve->lock) __acquires(&tve->lock)
{ {
...@@ -270,15 +276,14 @@ static struct drm_encoder *imx_tve_connector_best_encoder( ...@@ -270,15 +276,14 @@ static struct drm_encoder *imx_tve_connector_best_encoder(
{ {
struct imx_tve *tve = con_to_tve(connector); struct imx_tve *tve = con_to_tve(connector);
return &tve->imx_encoder.encoder; return &tve->encoder;
} }
static void imx_tve_encoder_mode_set(struct drm_encoder *encoder, static void imx_tve_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *orig_mode, struct drm_display_mode *orig_mode,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_tve *tve = enc_to_tve(encoder);
struct imx_tve *tve = imx_enc_to_tve(imx_encoder);
unsigned long rounded_rate; unsigned long rounded_rate;
unsigned long rate; unsigned long rate;
int div = 1; int div = 1;
...@@ -315,20 +320,32 @@ static void imx_tve_encoder_mode_set(struct drm_encoder *encoder, ...@@ -315,20 +320,32 @@ static void imx_tve_encoder_mode_set(struct drm_encoder *encoder,
static void imx_tve_encoder_enable(struct drm_encoder *encoder) static void imx_tve_encoder_enable(struct drm_encoder *encoder)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_tve *tve = enc_to_tve(encoder);
struct imx_tve *tve = imx_enc_to_tve(imx_encoder);
tve_enable(tve); tve_enable(tve);
} }
static void imx_tve_encoder_disable(struct drm_encoder *encoder) static void imx_tve_encoder_disable(struct drm_encoder *encoder)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_tve *tve = enc_to_tve(encoder);
struct imx_tve *tve = imx_enc_to_tve(imx_encoder);
tve_disable(tve); 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 = { static const struct drm_connector_funcs imx_tve_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms, .dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
...@@ -353,6 +370,7 @@ static const struct drm_encoder_helper_funcs imx_tve_encoder_helper_funcs = { ...@@ -353,6 +370,7 @@ static const struct drm_encoder_helper_funcs imx_tve_encoder_helper_funcs = {
.mode_set = imx_tve_encoder_mode_set, .mode_set = imx_tve_encoder_mode_set,
.enable = imx_tve_encoder_enable, .enable = imx_tve_encoder_enable,
.disable = imx_tve_encoder_disable, .disable = imx_tve_encoder_disable,
.atomic_check = imx_tve_atomic_check,
}; };
static irqreturn_t imx_tve_irq_handler(int irq, void *data) static irqreturn_t imx_tve_irq_handler(int irq, void *data)
...@@ -470,14 +488,12 @@ static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve) ...@@ -470,14 +488,12 @@ static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve)
encoder_type = tve->mode == TVE_MODE_VGA ? encoder_type = tve->mode == TVE_MODE_VGA ?
DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC; DRM_MODE_ENCODER_DAC : DRM_MODE_ENCODER_TVDAC;
ret = imx_drm_encoder_parse_of(drm, &tve->imx_encoder.encoder, ret = imx_drm_encoder_parse_of(drm, &tve->encoder, tve->dev->of_node);
tve->dev->of_node);
if (ret) if (ret)
return ret; return ret;
drm_encoder_helper_add(&tve->imx_encoder.encoder, drm_encoder_helper_add(&tve->encoder, &imx_tve_encoder_helper_funcs);
&imx_tve_encoder_helper_funcs); drm_encoder_init(drm, &tve->encoder, &imx_tve_encoder_funcs,
drm_encoder_init(drm, &tve->imx_encoder.encoder, &imx_tve_encoder_funcs,
encoder_type, NULL); encoder_type, NULL);
drm_connector_helper_add(&tve->connector, drm_connector_helper_add(&tve->connector,
...@@ -485,8 +501,7 @@ static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve) ...@@ -485,8 +501,7 @@ static int imx_tve_register(struct drm_device *drm, struct imx_tve *tve)
drm_connector_init(drm, &tve->connector, &imx_tve_connector_funcs, drm_connector_init(drm, &tve->connector, &imx_tve_connector_funcs,
DRM_MODE_CONNECTOR_VGA); DRM_MODE_CONNECTOR_VGA);
drm_mode_connector_attach_encoder(&tve->connector, drm_mode_connector_attach_encoder(&tve->connector, &tve->encoder);
&tve->imx_encoder.encoder);
return 0; return 0;
} }
...@@ -564,7 +579,7 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) ...@@ -564,7 +579,7 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
if (tve->mode == TVE_MODE_VGA) { if (tve->mode == TVE_MODE_VGA) {
ret = of_property_read_u32(np, "fsl,hsync-pin", ret = of_property_read_u32(np, "fsl,hsync-pin",
&tve->imx_encoder.di_hsync_pin); &tve->di_hsync_pin);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "failed to get vsync pin\n"); dev_err(dev, "failed to get vsync pin\n");
...@@ -572,14 +587,12 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data) ...@@ -572,14 +587,12 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
} }
ret |= of_property_read_u32(np, "fsl,vsync-pin", ret |= of_property_read_u32(np, "fsl,vsync-pin",
&tve->imx_encoder.di_vsync_pin); &tve->di_vsync_pin);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "failed to get vsync pin\n"); dev_err(dev, "failed to get vsync pin\n");
return ret; return ret;
} }
tve->imx_encoder.bus_format = MEDIA_BUS_FMT_GBR888_1X24;
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
...@@ -668,7 +681,7 @@ static void imx_tve_unbind(struct device *dev, struct device *master, ...@@ -668,7 +681,7 @@ static void imx_tve_unbind(struct device *dev, struct device *master,
struct imx_tve *tve = dev_get_drvdata(dev); struct imx_tve *tve = dev_get_drvdata(dev);
tve->connector.funcs->destroy(&tve->connector); tve->connector.funcs->destroy(&tve->connector);
tve->imx_encoder.encoder.funcs->destroy(&tve->imx_encoder.encoder); tve->encoder.funcs->destroy(&tve->encoder);
if (!IS_ERR(tve->dac_reg)) if (!IS_ERR(tve->dac_reg))
regulator_disable(tve->dac_reg); regulator_disable(tve->dac_reg);
......
...@@ -75,13 +75,56 @@ static void ipu_crtc_disable(struct drm_crtc *crtc) ...@@ -75,13 +75,56 @@ static void ipu_crtc_disable(struct drm_crtc *crtc)
spin_unlock_irq(&crtc->dev->event_lock); spin_unlock_irq(&crtc->dev->event_lock);
} }
static void imx_drm_crtc_reset(struct drm_crtc *crtc)
{
struct imx_crtc_state *state;
if (crtc->state) {
if (crtc->state->mode_blob)
drm_property_unreference_blob(crtc->state->mode_blob);
state = to_imx_crtc_state(crtc->state);
memset(state, 0, sizeof(*state));
} else {
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return;
crtc->state = &state->base;
}
state->base.crtc = crtc;
}
static struct drm_crtc_state *imx_drm_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct imx_crtc_state *state;
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 = { static const struct drm_crtc_funcs ipu_crtc_funcs = {
.set_config = drm_atomic_helper_set_config, .set_config = drm_atomic_helper_set_config,
.destroy = drm_crtc_cleanup, .destroy = drm_crtc_cleanup,
.page_flip = drm_atomic_helper_page_flip, .page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset, .reset = imx_drm_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_duplicate_state = imx_drm_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .atomic_destroy_state = imx_drm_crtc_destroy_state,
}; };
static irqreturn_t ipu_irq_handler(int irq, void *dev_id) static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
...@@ -142,9 +185,9 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc) ...@@ -142,9 +185,9 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc)
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct imx_drm_encoder *imx_encoder = NULL;
struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
struct drm_display_mode *mode = &crtc->state->adjusted_mode; 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 = {}; struct ipu_di_signal_cfg sig_cfg = {};
unsigned long encoder_types = 0; unsigned long encoder_types = 0;
...@@ -154,10 +197,8 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc) ...@@ -154,10 +197,8 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc)
mode->vdisplay); mode->vdisplay);
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc == crtc) { if (encoder->crtc == crtc)
encoder_types |= BIT(encoder->encoder_type); encoder_types |= BIT(encoder->encoder_type);
imx_encoder = enc_to_imx_enc(encoder);
}
} }
dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n", dev_dbg(ipu_crtc->dev, "%s: attached to encoder types 0x%lx\n",
...@@ -176,20 +217,20 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc) ...@@ -176,20 +217,20 @@ static void ipu_crtc_mode_set_nofb(struct drm_crtc *crtc)
else else
sig_cfg.clkflags = 0; sig_cfg.clkflags = 0;
sig_cfg.enable_pol = !(imx_encoder->bus_flags & DRM_BUS_FLAG_DE_LOW); sig_cfg.enable_pol = !(imx_crtc_state->bus_flags & DRM_BUS_FLAG_DE_LOW);
/* Default to driving pixel data on negative clock edges */ /* Default to driving pixel data on negative clock edges */
sig_cfg.clk_pol = !!(imx_encoder->bus_flags & sig_cfg.clk_pol = !!(imx_crtc_state->bus_flags &
DRM_BUS_FLAG_PIXDATA_POSEDGE); DRM_BUS_FLAG_PIXDATA_POSEDGE);
sig_cfg.bus_format = imx_encoder->bus_format; sig_cfg.bus_format = imx_crtc_state->bus_format;
sig_cfg.v_to_h_sync = 0; sig_cfg.v_to_h_sync = 0;
sig_cfg.hsync_pin = imx_encoder->di_hsync_pin; sig_cfg.hsync_pin = imx_crtc_state->di_hsync_pin;
sig_cfg.vsync_pin = imx_encoder->di_vsync_pin; sig_cfg.vsync_pin = imx_crtc_state->di_vsync_pin;
drm_display_mode_to_videomode(mode, &sig_cfg.mode); drm_display_mode_to_videomode(mode, &sig_cfg.mode);
ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di,
mode->flags & DRM_MODE_FLAG_INTERLACE, mode->flags & DRM_MODE_FLAG_INTERLACE,
imx_encoder->bus_format, mode->hdisplay); imx_crtc_state->bus_format, mode->hdisplay);
ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg); ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
} }
......
...@@ -27,19 +27,23 @@ ...@@ -27,19 +27,23 @@
#include "imx-drm.h" #include "imx-drm.h"
#define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector) #define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector)
#define imx_enc_to_imxpd(x) \
container_of(x, struct imx_parallel_display, imx_encoder)
struct imx_parallel_display { struct imx_parallel_display {
struct drm_connector connector; struct drm_connector connector;
struct imx_drm_encoder imx_encoder; struct drm_encoder encoder;
struct device *dev; struct device *dev;
void *edid; void *edid;
int edid_len; int edid_len;
u32 bus_format;
struct drm_display_mode mode; struct drm_display_mode mode;
struct drm_panel *panel; struct drm_panel *panel;
}; };
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( static enum drm_connector_status imx_pd_connector_detect(
struct drm_connector *connector, bool force) struct drm_connector *connector, bool force)
{ {
...@@ -54,12 +58,7 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector) ...@@ -54,12 +58,7 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector)
if (imxpd->panel && imxpd->panel->funcs && if (imxpd->panel && imxpd->panel->funcs &&
imxpd->panel->funcs->get_modes) { imxpd->panel->funcs->get_modes) {
struct drm_display_info *di = &connector->display_info;
num_modes = imxpd->panel->funcs->get_modes(imxpd->panel); num_modes = imxpd->panel->funcs->get_modes(imxpd->panel);
if (!imxpd->imx_encoder.bus_format && di->num_bus_formats)
imxpd->imx_encoder.bus_format = di->bus_formats[0];
imxpd->imx_encoder.bus_flags = di->bus_flags;
if (num_modes > 0) if (num_modes > 0)
return num_modes; return num_modes;
} }
...@@ -89,13 +88,12 @@ static struct drm_encoder *imx_pd_connector_best_encoder( ...@@ -89,13 +88,12 @@ static struct drm_encoder *imx_pd_connector_best_encoder(
{ {
struct imx_parallel_display *imxpd = con_to_imxpd(connector); struct imx_parallel_display *imxpd = con_to_imxpd(connector);
return &imxpd->imx_encoder.encoder; return &imxpd->encoder;
} }
static void imx_pd_encoder_enable(struct drm_encoder *encoder) static void imx_pd_encoder_enable(struct drm_encoder *encoder)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
struct imx_parallel_display *imxpd = imx_enc_to_imxpd(imx_encoder);
drm_panel_prepare(imxpd->panel); drm_panel_prepare(imxpd->panel);
drm_panel_enable(imxpd->panel); drm_panel_enable(imxpd->panel);
...@@ -103,13 +101,31 @@ static void imx_pd_encoder_enable(struct drm_encoder *encoder) ...@@ -103,13 +101,31 @@ static void imx_pd_encoder_enable(struct drm_encoder *encoder)
static void imx_pd_encoder_disable(struct drm_encoder *encoder) static void imx_pd_encoder_disable(struct drm_encoder *encoder)
{ {
struct imx_drm_encoder *imx_encoder = enc_to_imx_enc(encoder); struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
struct imx_parallel_display *imxpd = imx_enc_to_imxpd(imx_encoder);
drm_panel_disable(imxpd->panel); drm_panel_disable(imxpd->panel);
drm_panel_unprepare(imxpd->panel); 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 = { static const struct drm_connector_funcs imx_pd_connector_funcs = {
.dpms = drm_atomic_helper_connector_dpms, .dpms = drm_atomic_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
...@@ -132,15 +148,16 @@ static const struct drm_encoder_funcs imx_pd_encoder_funcs = { ...@@ -132,15 +148,16 @@ static const struct drm_encoder_funcs imx_pd_encoder_funcs = {
static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = { static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
.enable = imx_pd_encoder_enable, .enable = imx_pd_encoder_enable,
.disable = imx_pd_encoder_disable, .disable = imx_pd_encoder_disable,
.atomic_check = imx_pd_encoder_atomic_check,
}; };
static int imx_pd_register(struct drm_device *drm, static int imx_pd_register(struct drm_device *drm,
struct imx_parallel_display *imxpd) struct imx_parallel_display *imxpd)
{ {
struct drm_encoder *encoder = &imxpd->encoder;
int ret; int ret;
ret = imx_drm_encoder_parse_of(drm, &imxpd->imx_encoder.encoder, ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node);
imxpd->dev->of_node);
if (ret) if (ret)
return ret; return ret;
...@@ -151,10 +168,9 @@ static int imx_pd_register(struct drm_device *drm, ...@@ -151,10 +168,9 @@ static int imx_pd_register(struct drm_device *drm,
*/ */
imxpd->connector.dpms = DRM_MODE_DPMS_OFF; imxpd->connector.dpms = DRM_MODE_DPMS_OFF;
drm_encoder_helper_add(&imxpd->imx_encoder.encoder, drm_encoder_helper_add(encoder, &imx_pd_encoder_helper_funcs);
&imx_pd_encoder_helper_funcs); drm_encoder_init(drm, encoder, &imx_pd_encoder_funcs,
drm_encoder_init(drm, &imxpd->imx_encoder.encoder, DRM_MODE_ENCODER_NONE, NULL);
&imx_pd_encoder_funcs, DRM_MODE_ENCODER_NONE, NULL);
drm_connector_helper_add(&imxpd->connector, drm_connector_helper_add(&imxpd->connector,
&imx_pd_connector_helper_funcs); &imx_pd_connector_helper_funcs);
...@@ -164,8 +180,7 @@ static int imx_pd_register(struct drm_device *drm, ...@@ -164,8 +180,7 @@ static int imx_pd_register(struct drm_device *drm,
if (imxpd->panel) if (imxpd->panel)
drm_panel_attach(imxpd->panel, &imxpd->connector); drm_panel_attach(imxpd->panel, &imxpd->connector);
drm_mode_connector_attach_encoder(&imxpd->connector, drm_mode_connector_attach_encoder(&imxpd->connector, encoder);
&imxpd->imx_encoder.encoder);
return 0; return 0;
} }
...@@ -200,9 +215,7 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) ...@@ -200,9 +215,7 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
else if (!strcmp(fmt, "lvds666")) else if (!strcmp(fmt, "lvds666"))
bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI; bus_format = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
} }
imxpd->imx_encoder.bus_format = bus_format; imxpd->bus_format = bus_format;
imxpd->imx_encoder.di_hsync_pin = 2;
imxpd->imx_encoder.di_vsync_pin = 3;
/* port@1 is the output port */ /* port@1 is the output port */
ep = of_graph_get_endpoint_by_regs(np, 1, -1); ep = of_graph_get_endpoint_by_regs(np, 1, -1);
...@@ -235,7 +248,7 @@ static void imx_pd_unbind(struct device *dev, struct device *master, ...@@ -235,7 +248,7 @@ static void imx_pd_unbind(struct device *dev, struct device *master,
{ {
struct imx_parallel_display *imxpd = dev_get_drvdata(dev); struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
imxpd->imx_encoder.encoder.funcs->destroy(&imxpd->imx_encoder.encoder); imxpd->encoder.funcs->destroy(&imxpd->encoder);
imxpd->connector.funcs->destroy(&imxpd->connector); imxpd->connector.funcs->destroy(&imxpd->connector);
kfree(imxpd->edid); kfree(imxpd->edid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment