Merge branch 'imx-drm-fixes' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into staging-next

imx-drm fixes from Russell
This commit is contained in:
Greg Kroah-Hartman 2014-05-06 06:26:31 -07:00
commit c4784756a5
9 changed files with 196 additions and 62 deletions

View file

@ -76,6 +76,7 @@ enum ipu_channel_irq {
IPU_IRQ_EOS = 192,
};
int ipu_map_irq(struct ipu_soc *ipu, int irq);
int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
enum ipu_channel_irq irq);
@ -114,8 +115,10 @@ struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel);
void ipu_dc_put(struct ipu_dc *dc);
int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
u32 pixel_fmt, u32 width);
void ipu_dc_enable(struct ipu_soc *ipu);
void ipu_dc_enable_channel(struct ipu_dc *dc);
void ipu_dc_disable_channel(struct ipu_dc *dc);
void ipu_dc_disable(struct ipu_soc *ipu);
/*
* IPU Display Interface (di) functions
@ -152,8 +155,10 @@ void ipu_dmfc_put(struct dmfc_channel *dmfc);
struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow);
void ipu_dp_put(struct ipu_dp *);
int ipu_dp_enable(struct ipu_soc *ipu);
int ipu_dp_enable_channel(struct ipu_dp *dp);
void ipu_dp_disable_channel(struct ipu_dp *dp);
void ipu_dp_disable(struct ipu_soc *ipu);
int ipu_dp_setup_channel(struct ipu_dp *dp,
enum ipu_color_space in, enum ipu_color_space out);
int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);

View file

@ -697,6 +697,12 @@ int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
}
EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno)
{
return (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(chno)) & idma_mask(chno));
}
EXPORT_SYMBOL_GPL(ipu_idmac_channel_busy);
int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
{
struct ipu_soc *ipu = channel->ipu;
@ -714,6 +720,22 @@ int ipu_idmac_wait_busy(struct ipuv3_channel *channel, int ms)
}
EXPORT_SYMBOL_GPL(ipu_idmac_wait_busy);
int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms)
{
unsigned long timeout;
timeout = jiffies + msecs_to_jiffies(ms);
ipu_cm_write(ipu, BIT(irq % 32), IPU_INT_STAT(irq / 32));
while (!(ipu_cm_read(ipu, IPU_INT_STAT(irq / 32) & BIT(irq % 32)))) {
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
cpu_relax();
}
return 0;
}
EXPORT_SYMBOL_GPL(ipu_wait_interrupt);
int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
{
struct ipu_soc *ipu = channel->ipu;
@ -934,15 +956,22 @@ static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
int ipu_map_irq(struct ipu_soc *ipu, int irq)
{
int virq;
virq = irq_linear_revmap(ipu->domain, irq);
if (!virq)
virq = irq_create_mapping(ipu->domain, irq);
return virq;
}
EXPORT_SYMBOL_GPL(ipu_map_irq);
int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
enum ipu_channel_irq irq_type)
{
int irq = irq_linear_revmap(ipu->domain, irq_type + channel->num);
if (!irq)
irq = irq_create_mapping(ipu->domain, irq_type + channel->num);
return irq;
return ipu_map_irq(ipu, irq_type + channel->num);
}
EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);

View file

@ -18,6 +18,7 @@
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include "../imx-drm.h"
@ -111,6 +112,9 @@ struct ipu_dc_priv {
struct device *dev;
struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
struct mutex mutex;
struct completion comp;
int dc_irq;
int dp_irq;
};
static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
@ -223,12 +227,16 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
writel(0x0, dc->base + DC_WR_CH_ADDR);
writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di));
ipu_module_enable(priv->ipu, IPU_CONF_DC_EN);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
void ipu_dc_enable(struct ipu_soc *ipu)
{
ipu_module_enable(ipu, IPU_CONF_DC_EN);
}
EXPORT_SYMBOL_GPL(ipu_dc_enable);
void ipu_dc_enable_channel(struct ipu_dc *dc)
{
int di;
@ -242,41 +250,55 @@ void ipu_dc_enable_channel(struct ipu_dc *dc)
}
EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
static irqreturn_t dc_irq_handler(int irq, void *dev_id)
{
struct ipu_dc *dc = dev_id;
u32 reg;
reg = readl(dc->base + DC_WR_CH_CONF);
reg &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
writel(reg, dc->base + DC_WR_CH_CONF);
/* The Freescale BSP kernel clears DIx_COUNTER_RELEASE here */
complete(&dc->priv->comp);
return IRQ_HANDLED;
}
void ipu_dc_disable_channel(struct ipu_dc *dc)
{
struct ipu_dc_priv *priv = dc->priv;
int irq, ret;
u32 val;
int irq = 0, timeout = 50;
/* TODO: Handle MEM_FG_SYNC differently from MEM_BG_SYNC */
if (dc->chno == 1)
irq = IPU_IRQ_DC_FC_1;
irq = priv->dc_irq;
else if (dc->chno == 5)
irq = IPU_IRQ_DP_SF_END;
irq = priv->dp_irq;
else
return;
/* should wait for the interrupt here */
mdelay(50);
init_completion(&priv->comp);
enable_irq(irq);
ret = wait_for_completion_timeout(&priv->comp, msecs_to_jiffies(50));
disable_irq(irq);
if (ret <= 0) {
dev_warn(priv->dev, "DC stop timeout after 50 ms\n");
if (dc->di == 0)
val = 0x00000002;
else
val = 0x00000020;
/* Wait for DC triple buffer to empty */
while ((readl(priv->dc_reg + DC_STAT) & val) != val) {
usleep_range(2000, 20000);
timeout -= 2;
if (timeout <= 0)
break;
val = readl(dc->base + DC_WR_CH_CONF);
val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
writel(val, dc->base + DC_WR_CH_CONF);
}
val = readl(dc->base + DC_WR_CH_CONF);
val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
writel(val, dc->base + DC_WR_CH_CONF);
}
EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
void ipu_dc_disable(struct ipu_soc *ipu)
{
ipu_module_disable(ipu, IPU_CONF_DC_EN);
}
EXPORT_SYMBOL_GPL(ipu_dc_disable);
static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
int byte_num, int offset, int mask)
{
@ -343,7 +365,7 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
struct ipu_dc_priv *priv;
static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
0x78, 0, 0x94, 0xb4};
int i;
int i, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@ -364,6 +386,23 @@ int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
priv->channels[i].base = priv->dc_reg + channel_offsets[i];
}
priv->dc_irq = ipu_map_irq(ipu, IPU_IRQ_DC_FC_1);
if (!priv->dc_irq)
return -EINVAL;
ret = devm_request_irq(dev, priv->dc_irq, dc_irq_handler, 0, NULL,
&priv->channels[1]);
if (ret < 0)
return ret;
disable_irq(priv->dc_irq);
priv->dp_irq = ipu_map_irq(ipu, IPU_IRQ_DP_SF_END);
if (!priv->dp_irq)
return -EINVAL;
ret = devm_request_irq(dev, priv->dp_irq, dc_irq_handler, 0, NULL,
&priv->channels[5]);
if (ret < 0)
return ret;
disable_irq(priv->dp_irq);
writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
DC_WR_CH_CONF_PROG_DI_ID,
priv->channels[1].base + DC_WR_CH_CONF);

View file

@ -595,7 +595,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
}
}
if (!sig->clk_pol)
if (sig->clk_pol)
di_gen |= DI_GEN_POLARITY_DISP_CLK;
ipu_di_write(di, di_gen, DI_GENERAL);

View file

@ -28,7 +28,12 @@
#define DMFC_GENERAL1 0x0014
#define DMFC_GENERAL2 0x0018
#define DMFC_IC_CTRL 0x001c
#define DMFC_STAT 0x0020
#define DMFC_WR_CHAN_ALT 0x0020
#define DMFC_WR_CHAN_DEF_ALT 0x0024
#define DMFC_DP_CHAN_ALT 0x0028
#define DMFC_DP_CHAN_DEF_ALT 0x002c
#define DMFC_GENERAL1_ALT 0x0030
#define DMFC_STAT 0x0034
#define DMFC_WR_CHAN_1_28 0
#define DMFC_WR_CHAN_2_41 8
@ -133,6 +138,20 @@ int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
}
EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv)
{
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) {
if (time_after(jiffies, timeout)) {
dev_warn(priv->dev,
"Timeout waiting for DMFC FIFOs to clear\n");
break;
}
cpu_relax();
}
}
void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
{
struct ipu_dmfc_priv *priv = dmfc->priv;
@ -141,8 +160,10 @@ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
priv->use_count--;
if (!priv->use_count)
if (!priv->use_count) {
ipu_dmfc_wait_fifos(priv);
ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
}
if (priv->use_count < 0)
priv->use_count = 0;

View file

@ -215,10 +215,9 @@ int ipu_dp_setup_channel(struct ipu_dp *dp,
}
EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
int ipu_dp_enable_channel(struct ipu_dp *dp)
int ipu_dp_enable(struct ipu_soc *ipu)
{
struct ipu_flow *flow = to_flow(dp);
struct ipu_dp_priv *priv = flow->priv;
struct ipu_dp_priv *priv = ipu->dp_priv;
mutex_lock(&priv->mutex);
@ -227,15 +226,28 @@ int ipu_dp_enable_channel(struct ipu_dp *dp)
priv->use_count++;
if (dp->foreground) {
u32 reg;
mutex_unlock(&priv->mutex);
reg = readl(flow->base + DP_COM_CONF);
reg |= DP_COM_CONF_FG_EN;
writel(reg, flow->base + DP_COM_CONF);
return 0;
}
EXPORT_SYMBOL_GPL(ipu_dp_enable);
ipu_srm_dp_sync_update(priv->ipu);
}
int ipu_dp_enable_channel(struct ipu_dp *dp)
{
struct ipu_flow *flow = to_flow(dp);
struct ipu_dp_priv *priv = flow->priv;
u32 reg;
if (!dp->foreground)
return 0;
mutex_lock(&priv->mutex);
reg = readl(flow->base + DP_COM_CONF);
reg |= DP_COM_CONF_FG_EN;
writel(reg, flow->base + DP_COM_CONF);
ipu_srm_dp_sync_update(priv->ipu);
mutex_unlock(&priv->mutex);
@ -247,26 +259,39 @@ void ipu_dp_disable_channel(struct ipu_dp *dp)
{
struct ipu_flow *flow = to_flow(dp);
struct ipu_dp_priv *priv = flow->priv;
u32 reg, csc;
if (!dp->foreground)
return;
mutex_lock(&priv->mutex);
reg = readl(flow->base + DP_COM_CONF);
csc = reg & DP_COM_CONF_CSC_DEF_MASK;
if (csc == DP_COM_CONF_CSC_DEF_FG)
reg &= ~DP_COM_CONF_CSC_DEF_MASK;
reg &= ~DP_COM_CONF_FG_EN;
writel(reg, flow->base + DP_COM_CONF);
writel(0, flow->base + DP_FG_POS);
ipu_srm_dp_sync_update(priv->ipu);
if (ipu_idmac_channel_busy(priv->ipu, IPUV3_CHANNEL_MEM_BG_SYNC))
ipu_wait_interrupt(priv->ipu, IPU_IRQ_DP_SF_END, 50);
mutex_unlock(&priv->mutex);
}
EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
void ipu_dp_disable(struct ipu_soc *ipu)
{
struct ipu_dp_priv *priv = ipu->dp_priv;
mutex_lock(&priv->mutex);
priv->use_count--;
if (dp->foreground) {
u32 reg, csc;
reg = readl(flow->base + DP_COM_CONF);
csc = reg & DP_COM_CONF_CSC_DEF_MASK;
if (csc == DP_COM_CONF_CSC_DEF_FG)
reg &= ~DP_COM_CONF_CSC_DEF_MASK;
reg &= ~DP_COM_CONF_FG_EN;
writel(reg, flow->base + DP_COM_CONF);
writel(0, flow->base + DP_FG_POS);
ipu_srm_dp_sync_update(priv->ipu);
}
if (!priv->use_count)
ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
@ -275,7 +300,7 @@ void ipu_dp_disable_channel(struct ipu_dp *dp)
mutex_unlock(&priv->mutex);
}
EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
EXPORT_SYMBOL_GPL(ipu_dp_disable);
struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
{

View file

@ -185,6 +185,9 @@ void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
bool ipu_idmac_channel_busy(struct ipu_soc *ipu, unsigned int chno);
int ipu_wait_interrupt(struct ipu_soc *ipu, int irq, int ms);
int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
unsigned long base, u32 module, struct clk *ipu_clk);
void ipu_di_exit(struct ipu_soc *ipu, int id);

View file

@ -60,24 +60,32 @@ struct ipu_crtc {
static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
{
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
if (ipu_crtc->enabled)
return;
ipu_di_enable(ipu_crtc->di);
ipu_dc_enable_channel(ipu_crtc->dc);
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);
ipu_crtc->enabled = 1;
}
static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
{
struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
if (!ipu_crtc->enabled)
return;
ipu_plane_disable(ipu_crtc->plane[0]);
/* 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);
ipu_crtc->enabled = 0;
}
@ -158,7 +166,7 @@ static int ipu_crtc_mode_set(struct drm_crtc *crtc,
sig_cfg.Vsync_pol = 1;
sig_cfg.enable_pol = 1;
sig_cfg.clk_pol = 1;
sig_cfg.clk_pol = 0;
sig_cfg.width = mode->hdisplay;
sig_cfg.height = mode->vdisplay;
sig_cfg.pixel_fmt = out_pixel_fmt;

View file

@ -239,6 +239,8 @@ err_out:
void ipu_plane_enable(struct ipu_plane *ipu_plane)
{
if (ipu_plane->dp)
ipu_dp_enable(ipu_plane->ipu);
ipu_dmfc_enable_channel(ipu_plane->dmfc);
ipu_idmac_enable_channel(ipu_plane->ipu_ch);
if (ipu_plane->dp)
@ -257,6 +259,8 @@ void ipu_plane_disable(struct ipu_plane *ipu_plane)
ipu_dp_disable_channel(ipu_plane->dp);
ipu_idmac_disable_channel(ipu_plane->ipu_ch);
ipu_dmfc_disable_channel(ipu_plane->dmfc);
if (ipu_plane->dp)
ipu_dp_disable(ipu_plane->ipu);
}
static void ipu_plane_dpms(struct ipu_plane *ipu_plane, int mode)