mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-27 06:34:11 +08:00
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:
commit
c4784756a5
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
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");
|
||||
|
||||
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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
mutex_unlock(&priv->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ipu_dp_enable);
|
||||
|
||||
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,14 +259,13 @@ 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);
|
||||
|
||||
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)
|
||||
@ -265,7 +276,21 @@ void ipu_dp_disable_channel(struct ipu_dp *dp)
|
||||
|
||||
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 (!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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user