* [PATCH 04/15] fbdev: sh_mipi_dsi: tidyup VMCTR2 parameter expression
From: Kuninori Morimoto @ 2011-11-09 4:34 UTC (permalink / raw)
To: linux-fbdev
VMCTR2 parameter will be supported more in the future.
1 << xx style is easy to understand.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
drivers/video/sh_mipi_dsi.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 94bb1bb..20ccc23 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -317,9 +317,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
* HSA period allowed, no commands in LP
*/
if (pdata->flags & SH_MIPI_DSI_HSABM)
- vmctr2 |= 0x20;
+ vmctr2 |= 1 << 5;
if (pdata->flags & SH_MIPI_DSI_HBPBM)
- vmctr2 |= 0x10;
+ vmctr2 |= 1 << 4;
iowrite32(vmctr2, mipi->linkbase + VMCTR2);
/*
--
1.7.5.4
^ permalink raw reply related
* [PATCH 03/15] fbdev: sh_mipi_dsi: typo fix of SH_MIPI_DSI_HBPBM
From: Kuninori Morimoto @ 2011-11-09 4:34 UTC (permalink / raw)
To: linux-fbdev
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
drivers/video/sh_mipi_dsi.c | 2 +-
include/video/sh_mipi_dsi.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 4aa5053..94bb1bb 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -318,7 +318,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
*/
if (pdata->flags & SH_MIPI_DSI_HSABM)
vmctr2 |= 0x20;
- if (pdata->flags & SH_MIPI_DSI_HSPBM)
+ if (pdata->flags & SH_MIPI_DSI_HBPBM)
vmctr2 |= 0x10;
iowrite32(vmctr2, mipi->linkbase + VMCTR2);
diff --git a/include/video/sh_mipi_dsi.h b/include/video/sh_mipi_dsi.h
index 6cb95c9..4e2bcb5 100644
--- a/include/video/sh_mipi_dsi.h
+++ b/include/video/sh_mipi_dsi.h
@@ -28,7 +28,7 @@ enum sh_mipi_dsi_data_fmt {
struct sh_mobile_lcdc_chan_cfg;
#define SH_MIPI_DSI_HSABM (1 << 0)
-#define SH_MIPI_DSI_HSPBM (1 << 1)
+#define SH_MIPI_DSI_HBPBM (1 << 1)
struct sh_mipi_dsi_info {
enum sh_mipi_dsi_data_fmt data_format;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 02/15] fbdev: sh_mipi_dsi: tidyup dsip_clk
From: Kuninori Morimoto @ 2011-11-09 4:33 UTC (permalink / raw)
To: linux-fbdev
dsipck clock is controled by CLKDEV_ICK_ID() in clock-shxxx.
dsi0p_clk/dsi1p_clk naming is not needed.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
arch/arm/mach-shmobile/clock-sh7372.c | 4 ++--
arch/arm/mach-shmobile/clock-sh73a0.c | 4 ++--
drivers/video/sh_mipi_dsi.c | 4 +---
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c
index 6697592..a736d94 100644
--- a/arch/arm/mach-shmobile/clock-sh7372.c
+++ b/arch/arm/mach-shmobile/clock-sh7372.c
@@ -612,8 +612,8 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("hdmi_clk", &div6_reparent_clks[DIV6_HDMI]),
CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSIT]),
CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSIT]),
- CLKDEV_ICK_ID("dsi0p_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]),
- CLKDEV_ICK_ID("dsi1p_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]),
+ CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]),
+ CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]),
/* MSTP32 clocks */
CLKDEV_DEV_ID("i2c-sh_mobile.2", &mstp_clks[MSTP001]), /* IIC2 */
diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index 61a846b..3da30ba 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -319,8 +319,8 @@ static struct clk_lookup lookups[] = {
CLKDEV_CON_ID("sdhi2_clk", &div6_clks[DIV6_SDHI2]),
CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSIT]),
CLKDEV_ICK_ID("dsit_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSIT]),
- CLKDEV_ICK_ID("dsi0p_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]),
- CLKDEV_ICK_ID("dsi1p_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]),
+ CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.0", &div6_clks[DIV6_DSI0P]),
+ CLKDEV_ICK_ID("dsip_clk", "sh-mipi-dsi.1", &div6_clks[DIV6_DSI1P]),
/* MSTP32 clocks */
CLKDEV_DEV_ID("i2c-sh_mobile.2", &mstp_clks[MSTP001]), /* I2C2 */
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 72ee96b..4aa5053 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -363,7 +363,6 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
unsigned long rate, f_current;
int idx = pdev->id, ret;
- char dsip_clk[] = "dsi.p_clk";
if (!res || !res2 || idx >= ARRAY_SIZE(mipi_dsi) || !pdata)
return -ENODEV;
@@ -428,8 +427,7 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "DSI-T clk %lu -> %lu\n", f_current, rate);
- sprintf(dsip_clk, "dsi%1.1dp_clk", idx);
- mipi->dsip_clk = clk_get(&pdev->dev, dsip_clk);
+ mipi->dsip_clk = clk_get(&pdev->dev, "dsip_clk");
if (IS_ERR(mipi->dsip_clk)) {
ret = PTR_ERR(mipi->dsip_clk);
goto eclkpget;
--
1.7.5.4
^ permalink raw reply related
* [PATCH 01/15] fbdev: sh_mobile_lcdcfb: fixup LDHAJR :: HSYNPAJ needs mask
From: Kuninori Morimoto @ 2011-11-09 4:33 UTC (permalink / raw)
To: linux-fbdev
LDHAJR register will be broken by hsync_pos without this patch
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
drivers/video/sh_mobile_lcdcfb.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index facffc2..1f49ab4 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -420,7 +420,7 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
tmp = ((display_var->xres & 7) << 24) |
((display_h_total & 7) << 16) |
((display_var->hsync_len & 7) << 8) |
- hsync_pos;
+ (hsync_pos & 7);
lcdc_write_chan(ch, LDHAJR, tmp);
}
--
1.7.5.4
^ permalink raw reply related
* [PATCH 0/15] fbdev: sh_mipi_dsi: care un-explained register settings
From: kuninori.morimoto.gx @ 2011-11-09 4:32 UTC (permalink / raw)
To: linux-fbdev
Dear Florian, Paul
These are sh_mipi_dsi update patches.
Kuninori Morimoto (15):
fbdev: sh_mobile_lcdcfb: fixup LDHAJR :: HSYNPAJ needs mask
fbdev: sh_mipi_dsi: tidyup dsip_clk
fbdev: sh_mipi_dsi: typo fix of SH_MIPI_DSI_HBPBM
fbdev: sh_mipi_dsi: tidyup VMCTR2 parameter expression
fbdev: sh_mipi_dsi: add SH_MIPI_DSI_HFPBM flag
fbdev: sh_mipi_dsi: add SH_MIPI_DSI_BL2E flag
fbdev: sh_mipi_dsi: add lane control support
fbdev: sh_mipi_dsi: add sync_pulses/sync_events/burst mode
fbdev: sh_mipi_dsi: add VMLEN1/VMLEN2 calculation
fbdev: sh_mipi_dsi: add set_dot_clock() for each platform
fbdev: sh_mipi_dsi: add HSxxCLK support
fbdev: sh_mipi_dsi: sh_mipi has pdata instead of dev
fbdev: sh_mipi_dsi: fixup setup timing of sh_mipi_setup()
fbdev: sh_mipi_dsi: fixup setup timing of SYSCONF
fbdev: sh_mipi_dsi: fixup setup timing DSICTRL
SH MIPI DSI has many registers, but some registers are not explained for detail in SH manual.
This time, I asked it to Renesas MIPI guys.
These patches include this result.
for example, if your board is using AP5R chip,
HS4divCLK is needed for 1920x1080p 60Hz output.
But you can NOT find its explain and calculation method on AP5R manual.
These are based on latest linus/master tree
>> Paul
Can you please check #2, #7, #8, #10, #11 pache which modify arch/arm/mach-shmobile/xxx
^ permalink raw reply
* [RFC] drivers/video: fsl-diu-fb: combine several allocated buffers into one
From: Timur Tabi @ 2011-11-08 23:04 UTC (permalink / raw)
To: linux-fbdev
(Florian: this patch is the first of my changes for 3.3, but because it's
so extensive, I wanted to get your feedback before I continue with the rest
of my changes).
The DIU driver allocates several different objects separately, many of
which are accessed directly by the hardware and must be aligned on various
boundaries. This results in multiple allocations and awkward data
structures used to keep track of all the pointers and physical addresses.
Instead, merge all of these objects into the fsl_diu_data structure, and
enforce the alignment within the structure. Only one physical address
needs to be remember, and a macro is used to calculate the physical
address of any field. Frame buffers are still allocated separately,
since their sizes vary with the resolutions.
Documentation for some affected data structures and variables is added.
The code assume that dma_alloc_coherent() will always return an aligned
memory block. This function uses a page allocator on every architecture
that has a DIU, so this is safe. The alternative is to allocate an extra
31 bytes that will never be used, and keep track of offsets that will
always be zero. However, to be future-proof, we still validate the
alignement.
Signed-off-by: Timur Tabi <timur@freescale.com>
---
drivers/video/fsl-diu-fb.c | 264 ++++++++++++++++++++------------------------
1 files changed, 118 insertions(+), 146 deletions(-)
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index a16beeb..68f24a5 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -36,11 +36,9 @@
#include <linux/fsl-diu-fb.h>
#include "edid.h"
-#define FSL_AOI_NUM 6 /* 5 AOIs and one dummy AOI */
- /* 1 for plane 0, 2 for plane 1&2 each */
+#define NUM_AOIS 5 /* 1 for plane 0, 2 for planes 1 & 2 each */
-/* HW cursor parameters */
-#define MAX_CURS 32
+#define MAX_CURS 32 /* HW cursor is 32 * 32 pixels, 16 bpp */
/* INT_STATUS/INT_MASK field descriptions */
#define INT_VSYNC 0x01 /* Vsync interrupt */
@@ -49,12 +47,6 @@
#define INT_PARERR 0x08 /* Display parameters error interrupt */
#define INT_LS_BF_VS 0x10 /* Lines before vsync. interrupt */
-struct diu_addr {
- void *vaddr; /* Virtual address */
- dma_addr_t paddr; /* Physical address */
- __u32 offset;
-};
-
/*
* List of supported video modes
*
@@ -330,22 +322,43 @@ static unsigned int d_cache_line_size;
static DEFINE_SPINLOCK(diu_lock);
+/**
+ * struct fsl_diu_data - per-DIU data structure
+ * @phys: physical address of this structure
+ * @fsl_diu_info: fb_info objects, one per AOI
+ * @dev_attr: sysfs structure
+ * @irq: IRQ
+ * @fb_enabled: TRUE if the DIU is enabled, FALSE if not
+ * @monitor_port: the monitor port this DIU is connected to
+ * @diu_reg: pointer to the DIU hardware registers
+ * @reg_lock: spinlock for register access
+ * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI
+ * dummy_ad: DIU Area Descriptor for the dummy AOI
+ * @ad[]: Area Descriptors for each real AOI
+ * @gamma: gamma color table
+ * @cursor: hardware cursor data
+ *
+ * This data structure must be allocated with 32-byte alignment, so that the
+ * internal fields can be aligned properly.
+ */
struct fsl_diu_data {
- struct fb_info *fsl_diu_info[FSL_AOI_NUM - 1];
- /*FSL_AOI_NUM has one dummy AOI */
+ dma_addr_t phys;
+ struct fb_info *fsl_diu_info[NUM_AOIS];
struct device_attribute dev_attr;
- struct diu_ad *dummy_ad;
- void *dummy_aoi_virt;
unsigned int irq;
int fb_enabled;
enum fsl_diu_monitor_port monitor_port;
struct diu __iomem *diu_reg;
spinlock_t reg_lock;
- struct diu_addr ad;
- struct diu_addr gamma;
- struct diu_addr pallete;
- struct diu_addr cursor;
-};
+ u8 dummy_aoi[4 * 4 * 4];
+ struct diu_ad dummy_ad __aligned(8);
+ struct diu_ad ad[NUM_AOIS] __aligned(8);
+ u8 gamma[256 * 3] __aligned(32);
+ u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32);
+} __aligned(32);
+
+/* Determine the physical address of a member of the fsl_diu_data structure */
+#define PHYS_ADDR(p, f) ((p)->phys + offsetof(struct fsl_diu_data, f))
enum mfb_index {
PLANE0 = 0, /* Plane 0, only one AOI that fills the screen */
@@ -355,6 +368,21 @@ enum mfb_index {
PLANE2_AOI1, /* Plane 2, second AOI */
};
+/**
+ * struct mfb_info - per-AOI data structure
+ * @index: the AOI index
+ * @id: the name of this AOI
+ * @registered: TRUE = this framebuffer has been registed with fbdev
+ * @pseudo_palette: the pseudo-palette
+ * @ad: pointer to the Area Descriptor for this AOI
+ * @cursor_reset: unusued
+ * @g_alpha: the global alpha value, can be set by ioctl
+ * @count: the number of times this framebuffer has been opened
+ * @x_aoi_d: X-offset of the AOI. 0 is the left edge of the physical screen
+ * @y_aoi_d: Y-offset of the AOI. 0 is the top edge of the physical screen
+ * @parent: pointer to fsl_diu_data structure
+ * @edid_data: EDID data
+ */
struct mfb_info {
enum mfb_index index;
char *id;
@@ -364,8 +392,8 @@ struct mfb_info {
int cursor_reset;
unsigned char g_alpha;
unsigned int count;
- int x_aoi_d; /* aoi display x offset to physical screen */
- int y_aoi_d; /* aoi display y offset to physical screen */
+ int x_aoi_d;
+ int y_aoi_d;
struct fsl_diu_data *parent;
u8 *edid_data;
};
@@ -528,7 +556,7 @@ static void fsl_diu_enable_panel(struct fb_info *info)
case PLANE1_AOI1:
pmfbi = machine_data->fsl_diu_info[1]->par;
ad->next_ad = 0;
- if (hw->desc[1] = machine_data->dummy_ad->paddr)
+ if (hw->desc[1] = machine_data->dummy_ad.paddr)
wr_reg_wa(&hw->desc[1], ad->paddr);
else /* AOI0 open */
pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
@@ -536,7 +564,7 @@ static void fsl_diu_enable_panel(struct fb_info *info)
case PLANE2_AOI1:
pmfbi = machine_data->fsl_diu_info[3]->par;
ad->next_ad = 0;
- if (hw->desc[2] = machine_data->dummy_ad->paddr)
+ if (hw->desc[2] = machine_data->dummy_ad.paddr)
wr_reg_wa(&hw->desc[2], ad->paddr);
else /* AOI0 was open */
pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
@@ -553,8 +581,8 @@ static void fsl_diu_disable_panel(struct fb_info *info)
switch (mfbi->index) {
case PLANE0:
- if (hw->desc[0] != machine_data->dummy_ad->paddr)
- wr_reg_wa(&hw->desc[0], machine_data->dummy_ad->paddr);
+ if (hw->desc[0] != machine_data->dummy_ad.paddr)
+ wr_reg_wa(&hw->desc[0], machine_data->dummy_ad.paddr);
break;
case PLANE1_AOI0:
cmfbi = machine_data->fsl_diu_info[2]->par;
@@ -562,7 +590,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);
/* move AOI1 to the first */
else /* AOI1 was closed */
- wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
+ wr_reg_wa(&hw->desc[1], machine_data->dummy_ad.paddr);
/* close AOI 0 */
break;
case PLANE2_AOI0:
@@ -571,7 +599,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);
/* move AOI1 to the first */
else /* AOI1 was closed */
- wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
+ wr_reg_wa(&hw->desc[2], machine_data->dummy_ad.paddr);
/* close AOI 0 */
break;
case PLANE1_AOI1:
@@ -582,7 +610,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
/* AOI0 is open, must be the first */
pmfbi->ad->next_ad = 0;
} else /* AOI1 is the first in the chain */
- wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
+ wr_reg_wa(&hw->desc[1], machine_data->dummy_ad.paddr);
/* close AOI 1 */
break;
case PLANE2_AOI1:
@@ -593,7 +621,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
/* AOI0 is open, must be the first */
pmfbi->ad->next_ad = 0;
} else /* AOI1 is the first in the chain */
- wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
+ wr_reg_wa(&hw->desc[2], machine_data->dummy_ad.paddr);
/* close AOI 1 */
break;
}
@@ -812,15 +840,15 @@ static void update_lcdc(struct fb_info *info)
struct fsl_diu_data *machine_data = mfbi->parent;
struct diu __iomem *hw;
int i, j;
- char __iomem *cursor_base, *gamma_table_base;
+ u8 *gamma_table_base;
u32 temp;
hw = machine_data->diu_reg;
diu_ops.set_monitor_port(machine_data->monitor_port);
- gamma_table_base = machine_data->gamma.vaddr;
- cursor_base = machine_data->cursor.vaddr;
+ gamma_table_base = machine_data->gamma;
+
/* Prep for DIU init - gamma table, cursor table */
for (i = 0; i <= 2; i++)
@@ -828,14 +856,14 @@ static void update_lcdc(struct fb_info *info)
*gamma_table_base++ = j;
diu_ops.set_gamma_table(machine_data->monitor_port,
- machine_data->gamma.vaddr);
+ machine_data->gamma);
disable_lcdc(info);
/* Program DIU registers */
- out_be32(&hw->gamma, machine_data->gamma.paddr);
- out_be32(&hw->cursor, machine_data->cursor.paddr);
+ out_be32(&hw->gamma, PHYS_ADDR(machine_data, gamma));
+ out_be32(&hw->cursor, PHYS_ADDR(machine_data, cursor));
out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */
out_be32(&hw->bgnd_wb, 0); /* BGND_WB */
@@ -1423,37 +1451,6 @@ static int fsl_diu_resume(struct platform_device *ofdev)
#define fsl_diu_resume NULL
#endif /* CONFIG_PM */
-/* Align to 64-bit(8-byte), 32-byte, etc. */
-static int allocate_buf(struct device *dev, struct diu_addr *buf, u32 size,
- u32 bytes_align)
-{
- u32 offset;
- dma_addr_t mask;
-
- buf->vaddr - dma_alloc_coherent(dev, size + bytes_align, &buf->paddr,
- GFP_DMA | __GFP_ZERO);
- if (!buf->vaddr)
- return -ENOMEM;
-
- mask = bytes_align - 1;
- offset = buf->paddr & mask;
- if (offset) {
- buf->offset = bytes_align - offset;
- buf->paddr = buf->paddr + offset;
- } else
- buf->offset = 0;
-
- return 0;
-}
-
-static void free_buf(struct device *dev, struct diu_addr *buf, u32 size,
- u32 bytes_align)
-{
- dma_free_coherent(dev, size + bytes_align, buf->vaddr,
- buf->paddr - buf->offset);
-}
-
static ssize_t store_monitor(struct device *device,
struct device_attribute *attr, const char *buf, size_t count)
{
@@ -1499,28 +1496,54 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct mfb_info *mfbi;
- phys_addr_t dummy_ad_addr = 0;
- int ret, i, error = 0;
struct fsl_diu_data *machine_data;
int diu_mode;
+ dma_addr_t phys_addr; /* physical addr of machine_data struct */
+ unsigned int i;
+ int ret;
- machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL);
+ machine_data = dma_alloc_coherent(&pdev->dev,
+ sizeof(struct fsl_diu_data), &phys_addr, GFP_DMA | __GFP_ZERO);
if (!machine_data)
return -ENOMEM;
+ machine_data->phys = phys_addr;
+
+ /*
+ * dma_alloc_coherent() uses a page allocator, so the address is
+ * always page-aligned. We need the memory to be 32-byte aligned,
+ * so that's good. However, if one day the allocator changes, we
+ * need to catch that. It's not worth the effort to handle unaligned
+ * alloctions now because it's highly unlikely to ever be a problem.
+ */
+ if ((unsigned long)machine_data & 31) {
+ dev_err(&pdev->dev, "misaligned allocation");
+ ret = -ENOMEM;
+ goto error;
+ }
spin_lock_init(&machine_data->reg_lock);
- for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
+ for (i = 0; i < NUM_AOIS; i++) {
machine_data->fsl_diu_info[i] framebuffer_alloc(sizeof(struct mfb_info), &pdev->dev);
if (!machine_data->fsl_diu_info[i]) {
dev_err(&pdev->dev, "cannot allocate memory\n");
ret = -ENOMEM;
- goto error2;
+ goto error;
}
+ /*
+ * We store the physical address of the AD in the reserved
+ * 'paddr' field of the AD itself.
+ */
+ machine_data->ad[i].paddr = PHYS_ADDR(machine_data, ad[i]);
+
+ machine_data->fsl_diu_info[i]->fix.smem_start = 0;
+
+ /* Initialize the AOI data structure */
mfbi = machine_data->fsl_diu_info[i]->par;
memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
mfbi->parent = machine_data;
+ mfbi->ad = &machine_data->ad[i];
if (mfbi->index = PLANE0) {
const u8 *prop;
@@ -1538,7 +1561,7 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
if (!machine_data->diu_reg) {
dev_err(&pdev->dev, "cannot map DIU registers\n");
ret = -EFAULT;
- goto error2;
+ goto error;
}
diu_mode = in_be32(&machine_data->diu_reg->diu_mode);
@@ -1555,41 +1578,16 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
}
machine_data->monitor_port = monitor_port;
- /* Area descriptor memory pool aligns to 64-bit boundary */
- if (allocate_buf(&pdev->dev, &machine_data->ad,
- sizeof(struct diu_ad) * FSL_AOI_NUM, 8))
- return -ENOMEM;
-
- /* Get memory for Gamma Table - 32-byte aligned memory */
- if (allocate_buf(&pdev->dev, &machine_data->gamma, 768, 32)) {
- ret = -ENOMEM;
- goto error;
- }
-
- /* For performance, cursor bitmap buffer aligns to 32-byte boundary */
- if (allocate_buf(&pdev->dev, &machine_data->cursor,
- MAX_CURS * MAX_CURS * 2, 32)) {
- ret = -ENOMEM;
- goto error;
- }
-
- i = ARRAY_SIZE(machine_data->fsl_diu_info);
- machine_data->dummy_ad = (struct diu_ad *)((u32)machine_data->ad.vaddr +
- machine_data->ad.offset) + i;
- machine_data->dummy_ad->paddr = machine_data->ad.paddr +
- i * sizeof(struct diu_ad);
- machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr);
- if (!machine_data->dummy_aoi_virt) {
- ret = -ENOMEM;
- goto error;
- }
- machine_data->dummy_ad->addr = cpu_to_le32(dummy_ad_addr);
- machine_data->dummy_ad->pix_fmt = 0x88882317;
- machine_data->dummy_ad->src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
- machine_data->dummy_ad->aoi_size = cpu_to_le32((4 << 16) | 2);
- machine_data->dummy_ad->offset_xyi = 0;
- machine_data->dummy_ad->offset_xyd = 0;
- machine_data->dummy_ad->next_ad = 0;
+ /* Initialize the dummy Area Descriptor */
+ machine_data->dummy_ad.addr + cpu_to_le32(PHYS_ADDR(machine_data, dummy_aoi));
+ machine_data->dummy_ad.pix_fmt = 0x88882317;
+ machine_data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
+ machine_data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) | 2);
+ machine_data->dummy_ad.offset_xyi = 0;
+ machine_data->dummy_ad.offset_xyd = 0;
+ machine_data->dummy_ad.next_ad = 0;
+ machine_data->dummy_ad.paddr = PHYS_ADDR(machine_data, dummy_ad);
/*
* Let DIU display splash screen if it was pre-initialized
@@ -1597,18 +1595,12 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
*/
if (diu_mode = MFB_MODE0)
out_be32(&machine_data->diu_reg->desc[0],
- machine_data->dummy_ad->paddr);
+ machine_data->dummy_ad.paddr);
- out_be32(&machine_data->diu_reg->desc[1], machine_data->dummy_ad->paddr);
- out_be32(&machine_data->diu_reg->desc[2], machine_data->dummy_ad->paddr);
+ out_be32(&machine_data->diu_reg->desc[1], machine_data->dummy_ad.paddr);
+ out_be32(&machine_data->diu_reg->desc[2], machine_data->dummy_ad.paddr);
- for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
- machine_data->fsl_diu_info[i]->fix.smem_start = 0;
- mfbi = machine_data->fsl_diu_info[i]->par;
- mfbi->ad = (struct diu_ad *)((u32)machine_data->ad.vaddr
- + machine_data->ad.offset) + i;
- mfbi->ad->paddr - machine_data->ad.paddr + i * sizeof(struct diu_ad);
+ for (i = 0; i < NUM_AOIS; i++) {
ret = install_fb(machine_data->fsl_diu_info[i]);
if (ret) {
dev_err(&pdev->dev, "could not register fb %d\n", i);
@@ -1626,9 +1618,8 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
machine_data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
machine_data->dev_attr.show = show_monitor;
machine_data->dev_attr.store = store_monitor;
- error = device_create_file(machine_data->fsl_diu_info[0]->dev,
- &machine_data->dev_attr);
- if (error) {
+ ret = device_create_file(&pdev->dev, &machine_data->dev_attr);
+ if (ret) {
dev_err(&pdev->dev, "could not create sysfs file %s\n",
machine_data->dev_attr.attr.name);
}
@@ -1640,52 +1631,33 @@ error:
for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
uninstall_fb(machine_data->fsl_diu_info[i]);
- if (machine_data->ad.vaddr)
- free_buf(&pdev->dev, &machine_data->ad,
- sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
- if (machine_data->gamma.vaddr)
- free_buf(&pdev->dev, &machine_data->gamma, 768, 32);
- if (machine_data->cursor.vaddr)
- free_buf(&pdev->dev, &machine_data->cursor,
- MAX_CURS * MAX_CURS * 2, 32);
- if (machine_data->dummy_aoi_virt)
- fsl_diu_free(machine_data->dummy_aoi_virt, 64);
iounmap(machine_data->diu_reg);
-error2:
for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
if (machine_data->fsl_diu_info[i])
framebuffer_release(machine_data->fsl_diu_info[i]);
- kfree(machine_data);
+
+ dma_free_coherent(&pdev->dev, sizeof(struct fsl_diu_data),
+ machine_data, machine_data->phys);
return ret;
}
static int fsl_diu_remove(struct platform_device *pdev)
{
- struct fsl_diu_data *machine_data;
- int i;
+ struct fsl_diu_data *machine_data = dev_get_drvdata(&pdev->dev);
+ unsigned int i;
- machine_data = dev_get_drvdata(&pdev->dev);
disable_lcdc(machine_data->fsl_diu_info[0]);
free_irq_local(machine_data);
for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
uninstall_fb(machine_data->fsl_diu_info[i]);
- if (machine_data->ad.vaddr)
- free_buf(&pdev->dev, &machine_data->ad,
- sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
- if (machine_data->gamma.vaddr)
- free_buf(&pdev->dev, &machine_data->gamma, 768, 32);
- if (machine_data->cursor.vaddr)
- free_buf(&pdev->dev, &machine_data->cursor,
- MAX_CURS * MAX_CURS * 2, 32);
- if (machine_data->dummy_aoi_virt)
- fsl_diu_free(machine_data->dummy_aoi_virt, 64);
iounmap(machine_data->diu_reg);
for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
if (machine_data->fsl_diu_info[i])
framebuffer_release(machine_data->fsl_diu_info[i]);
- kfree(machine_data);
+ dma_free_coherent(&pdev->dev, sizeof(struct fsl_diu_data),
+ machine_data, machine_data->phys);
return 0;
}
--
1.7.3.4
^ permalink raw reply related
* Re: [RFC] Virtual CRTCs (proposal + experimental code)
From: Ilija Hadzic @ 2011-11-07 13:52 UTC (permalink / raw)
To: Dave Airlie; +Cc: linux-fbdev, dri-devel
In-Reply-To: <CAPM=9tyHDGHrRt1uOYR6ksxpxbss7Cbq9brFjpLy1S+kOk9AVw@mail.gmail.com>
On Mon, 7 Nov 2011, Dave Airlie wrote:
>
> So I expect the way I'd like this to work, is that we have full blown drm
> drivers for all the devices and then some sort of linkage layer between them,
> so one driver can export crtcs from another, instead of having special case
> ctd drivers.
I agree and that is actually a long-term plan from our side. CTD
functionality should be integral part of existing drivers not the new
driver, unless there is a new functionality that makes sense as CTD-only
(like v4l2ctd).
In the world that I imagine, likage layer is VCRTCM. Unaccelerated
frabebuffer devices (UDL for example, but in general anything that "lives"
in fbdev world) can choose (based on some policy from userland) whether to
act as CTD driver and register themselves with VCRTCM (when they want
acceleration "assistance" from a GPU in the system) or to load as
normal fbdev devices (when they want to run on their own).
> Now I can see the reason for the v4l one, but I have for example
> a udl kms driver, and I'd like to have it work alongside this stuff,
> and userspace
> could bind the crtcs from it to another driver. I'm not sure how much work
> this would be or if its just a matter of adding a CTD interface to the udl kms
> device.
>
The only reason we wrote a new udlctd driver was because it was quicker
that way (we just ripped some code from udlfb driver and added our CTD
functionality).
The plan was always to merge udlctd and udlfb at some point, but first
I'd like to see how perceptive is the community to the concept. If the
concept makes sense, then we'll throw in enough programming to consolidate
the drivers. Nobody wants three competing drivers for the same device
(udlfb, your udl kms driver and our udlctd).
Speaking of udl driver, is your udl-v2 branch competing with udlfb?
Externally, they seem to do similar or the same thing, but one is based on
DRM (and I guess the required DDX is xf86-video-modesetting), while the
other is based on fbdev and the required DDX is xf8b-video-fbdev. While
from my perspective both could be consilidated with a CTD functionality,
but it makes me wonder in which direction is the community development
moving: is everything from fbdev-world moving under DRM as a set of
unaccelerated KMS drivers or is fbdev staying separate for good ?
Depending on what the trend is, one or the other udl driver (udl-v2 or
udlfb) will make more sense to be merged with udlctd.
-- Ilija
^ permalink raw reply
* Re: [RFC] Virtual CRTCs (proposal + experimental code)
From: Dave Airlie @ 2011-11-07 12:58 UTC (permalink / raw)
To: Ilija Hadzic; +Cc: linux-fbdev, dri-devel
In-Reply-To: <CA+4h6Hk3gE+8ou6NSMhoGJWZJ-5aM1OgC0HFoKs55fjfQ-qN8g@mail.gmail.com>
> In general any device that can do something good with rendered pixels
> can act as a CTD device, allowing a GPU to be an acceleration device
> for a less capable display device or (the opposite) a frame-buffer-based
> display device to be an expansion card for a GPU. Of course, for
> each display device, a driver would have to be made compatible with our
> new infrastructure (which is what we have done with DisplayLink driver
> and also wrote one "synthetic" driver to fake out a V4L2 device as a
> CTD device).
So I expect the way I'd like this to work, is that we have full blown drm
drivers for all the devices and then some sort of linkage layer between them,
so one driver can export crtcs from another, instead of having special case
ctd drivers. Now I can see the reason for the v4l one, but I have for example
a udl kms driver, and I'd like to have it work alongside this stuff,
and userspace
could bind the crtcs from it to another driver. I'm not sure how much work
this would be or if its just a matter of adding a CTD interface to the udl kms
device.
Dave
^ permalink raw reply
* RFC: abstract common HDMI code
From: Jun Nie @ 2011-11-07 10:20 UTC (permalink / raw)
To: linux-fbdev
[-- Attachment #1: Type: text/plain, Size: 41733 bytes --]
Hi everyone,
I am writing another HDMI driver for our new SOC and find that
sh_mobile_hdmi.c implemented some logic that can be reused by my
driver. I do not want to duplicated similar code and enlarge kernel
code size with it. So my plan is to abstract some common code to an
independent file to ease other developers. If you guys think it is
good to have a such common layer, I will put more effort on this and
contribute to the community. Also any help and co-work is warmly
welcomed!
The attachment is my every preliminary code that only support
video resolution change and hot-plug on my platform with my private
code base. Sorry for the lot of style issues and small bugs for my
hurry debug. In my code, video resolution interface is still based on
original /sys/class/graphics/fb1/mode and more HDMI specific item is
added in /sys/class/hdmi/, such as video ratio. EDID part is not
ported yet and use predefined mode list in the first step. ASoC can be
added later just as sh_mobile_hdmi.c does, CEC and DHCP are the same.
Thanks for any comments!
Jun
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
new file mode 100644
index 0000000..c845dc7
--- /dev/null
+++ b/drivers/video/hdmi.c
@@ -0,0 +1,675 @@
+/*
+ * HDMI Control Abstraction
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/freezer.h>
+#include <linux/hdmi.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/lcd.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define HDMI_DEBUG
+static int hdmi_core_debug = 5;
+#define hdmi_dprintk(level, fmt, arg...) if (hdmi_core_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt , __func__, ## arg)
+
+/* Define the max number of events to buffer */
+#define MAX_HDMI_EVENT_SIZE 16
+
+static int hdmidevno;
+
+/* ==================== info frame utility ===================== */
+
+int hdmi_vender_info_frame (struct hdmi_dev *hd, char buf[])
+{
+ buf[0] = 0x84;
+ buf[1] = 0x1;
+ buf[2] = 0xa;
+ buf[3] = 0x70;
+ buf[4] = 0x1;
+}
+EXPORT_SYMBOL_GPL(hdmi_vender_info_frame);
+
+int hdmi_avi_info_frame (struct hdmi_dev *hd, char buf[])
+{
+ int count;
+ char check_sum;
+
+ hdmi_dprintk(3, "pack avi infoframe\n");
+ /* packet header for AVI Packet in pkt1 */
+ /* Fix me. we should set bits according to format */
+ buf[0] = 0x82; /* InfoFrame Type, AVI frame */
+ buf[1] = 0x2; /* Version = 02 */
+ buf[2] = 0xd; /* Length = 13 */
+ buf[3] = 0x0; /* checksum */
+ buf[4] = 0x0; /* Fix to RGB format currently */
+ /*
+ buf[4] = 0x10; // RGB, Active Format(r0,r1,r2,r3) valid, no bar, no scan
+ buf[4] = 0x30; // HDMI_YUV422, Active Format(r0,r1,r2,r3) valid, no
bar, no scan
+ buf[4] = 0x50; // HDMI_YUV444, Active Format(r0,r1,r2,r3) valid, no
bar, no scan
+ */
+
+ buf[5] = 0xa8; /* ITU-R 709, 16:9 */
+ buf[6] = 0x20;
+ /* buf[6] = 0x0; // No IT content, xvYCC601, no scaling */
+ buf[7] = hd->mode_id;
+ /* limited YCC range, graphics, no repeat Pixel repitition factor */
+ buf[8] = hd->pixel_rept;
+
+ buf[9] = 0x0;
+ buf[10] = 0x0;
+ buf[11] = 0x0;
+ buf[12] = 0x0;
+ buf[13] = 0x0;
+ check_sum = 0;
+ for (count = 0; count < 14; count++)
+ check_sum += buf[count];
+ buf[3] = 0x100 - check_sum;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_avi_info_frame);
+
+int hdmi_get_freq (struct hdmi_dev *hd, u32 *need_freq)
+{
+ u64 div_result;
+ int freq;
+
+ /* Calc divider according to pclk in pico second */
+ div_result = 1000000000000ll;
+ do_div(div_result, cea_modes[hd->mode_id].pixclock);
+ freq = (u32)div_result;
+
+ hdmi_dprintk(3, "get base pclk %d\n", freq);
+
+ if (FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode)
+ freq >>= 1;
+
+ if (36 == hd->fb_info->var.bits_per_pixel)
+ freq += freq >> 1;
+ else if (30 == hd->fb_info->var.bits_per_pixel)
+ freq += freq >> 2;
+
+ /* We only support repeat=2/4 currently. Add more later */
+ if (!(hd->pixel_rept == 0 || hd->pixel_rept == 1
+ || hd->pixel_rept == 3)) {
+ hdmi_dprintk(3, "Not support pix repeat %d\n", hd->pixel_rept);
+ return -EINVAL;
+ }
+
+ if (1 == hd->pixel_rept)
+ {
+ if (!(FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode))
+ freq <<= 1;
+ } else if (3 == hd->pixel_rept) {
+ if (!(FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode))
+ freq <<= 2;
+ else
+ freq <<= 1;
+ }
+
+ hdmi_dprintk(3, "get hdmi pclk %d\n", freq);
+ *need_freq = freq;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_get_freq);
+
+/* ==================== video part ===================== */
+int hdmi_lcd_check_fb(struct lcd_device * ld, struct fb_info *fi)
+{
+ struct hdmi_dev *hd = lcd_get_data(ld);
+
+ hdmi_dprintk(3, "hdmi_lcd_check_fb..\n");
+ return 1;
+}
+
+/* We'd better update mode list in hot plug operation.
+ LCD framework does not implement get_mode yet */
+static int hdmi_lcd_get_mode(struct lcd_device *ld, struct fb_videomode *mode)
+{
+ struct hdmi_dev *hd = lcd_get_data(ld);
+
+ /* TODO: return parsed mode list */
+ return 0;
+}
+
+static int hdmi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *mode)
+{
+ //struct hdmi_lcd_data *data = lcd_get_data(lcd);
+ struct hdmi_dev *hd = lcd_get_data(ld);
+ cea_mode_id mode_id;
+
+ hdmi_dprintk(3, "hdmi_lcd_set_mode dev\n");
+ mode_id = fb_find_cea_mode_index(mode);
+ if (!mode_id) {
+ hdmi_dprintk(1, "unknown video mode %d\n", mode_id);
+ return -EINVAL;
+ }
+ /* If high level on detection signal, no connection */
+ if(! hd->ops->get_hpd(hd))
+ return 0;
+
+ hd->ops->set_mode(hd, mode_id);
+
+ if (hd->ops && hd->ops->set_enable)
+ hd->ops->set_enable(hd, 1);
+}
+
+static struct lcd_ops hdmi_lcd_ops = {
+ .get_mode = hdmi_lcd_get_mode,
+ .set_mode = hdmi_lcd_set_mode,
+ .check_fb = hdmi_lcd_check_fb,
+};
+
+static int hdmi_register_lcd(struct hdmi_dev *dev)
+{
+ int ret = 0;
+
+ hdmi_dprintk(3, "registering lcd device...\n");
+ /* FIX ME! How to hook expected fb if more than one fb exist? */
+ dev->lcd = lcd_device_register("hdmi-lcd", dev->dev, dev,
+ &hdmi_lcd_ops);
+
+ if (IS_ERR(dev->lcd)) {
+ ret = PTR_ERR(dev->lcd);
+ dev->lcd = NULL;
+ return ret;
+ }
+ hdmi_dprintk(3, "registering lcd device ok\n");
+ return 0;
+}
+
+/* ==================== audio part ===================== */
+/* ==================== cec part ===================== */
+/* ==================== edid part ===================== */
+
+/* ==================== hot plug part ===================== */
+static int hdmi_hotplug(struct hdmi_dev *hd)
+{
+ int ret;
+
+ msleep(100);
+ ret = hd->ops->get_hpd(hd);
+ if(!ret) {
+ hdmi_dprintk(3, "uplugged. dev %p\n", hd);
+ ret = hd->ops->set_enable(hd, 0);
+ lock_fb_info(hd->fb_info);
+ /* We should clear mode list here */
+ /* ... */
+ unlock_fb_info(hd->fb_info);
+ return 0;
+ }
+
+ ret = hd->ops->init(hd);
+ if (ret) {
+ hdmi_dprintk(1, "init fail\n");
+ }
+
+ if(hd->fb_info && hd->fb_info->mode) {
+ lock_fb_info(hd->fb_info);
+ /* Fix me! We should get EDID and parse mode list here */
+ /* But now we use fix mode list first before EDID util is ready */
+ /* ... */
+ unlock_fb_info(hd->fb_info);
+
+ ret = hdmi_lcd_set_mode(hd->lcd, hd->fb_info->mode);
+ if (ret) {
+ hdmi_dprintk(1, "fail to set video mode\n");
+ }
+ } else {
+ hdmi_dprintk(3, "no avaliable video mode\n");
+ }
+
+ if (hd->enable)
+ ret = hd->ops->set_enable(hd, 1);
+ return ret;
+}
+
+/* Handling hot plug HDCP and CEC */
+static int hdmi_event_thread(void *data)
+{
+ struct hdmi_dev *dev = (struct hdmi_dev *)data;
+ struct hdmi_event *evt;
+
+ while (!kthread_should_stop()) {
+
+ spin_lock_irq(&dev->lock);
+ if (list_empty(&dev->hdmi_evt_list)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(&dev->lock);
+ schedule();
+ continue;
+ }
+
+ evt = list_entry(dev->hdmi_evt_list.next, typeof(*evt), list);
+ list_del(&evt->list);
+ spin_unlock_irq(&dev->lock);
+ hdmi_dprintk(1, "Receive evt type %d\n", evt->type);
+
+ mutex_lock(&dev->hdmi_lock);
+ switch (evt->type) {
+ /* trigger gpio irq */
+ case HDMI_EV_PLUG:
+ hdmi_hotplug(dev);
+ break;
+ /* trigger from TV/CEC */
+ case HDMI_EV_CEC:
+ break;
+ default:
+ hdmi_dprintk(1, "Invalid event type %d\n", evt->type);
+ break;
+ }
+ mutex_unlock(&dev->hdmi_lock);
+ }
+
+ return 0;
+}
+
+/**
+ * hdmi_event_signal() - schedules the hdmi event thread
+ * @dev: the struct hdmi_dev device
+ *
+ * This routine will tell hdmi-core to start decoding stored ir data.
+ */
+void hdmi_event_signal(struct hdmi_dev *dev, enum hdmi_event_type type)
+{
+ unsigned long flags;
+ struct hdmi_event *evt;
+
+ hdmi_dprintk(1, "hdmi event %d stored, dev %p\n", type, dev);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if(HDMI_EV_PLUG == type) {
+ list_for_each_entry(evt, &dev->hdmi_evt_list, list) {
+ if (HDMI_EV_PLUG == evt->type) {
+ hdmi_dprintk(1, "pending hotplug event, ignore new ones\n");
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return;
+ }
+ }
+ }
+ evt = list_entry(dev->free_evt_list.next, struct hdmi_event, list);
+ evt->type = type;
+ list_move_tail(&evt->list, &dev->hdmi_evt_list);
+ wake_up_process(dev->thread);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+EXPORT_SYMBOL_GPL(hdmi_event_signal);
+
+/* ==================== sys fs part ===================== */
+#ifdef HDMI_DEBUG
+static ssize_t hdmi_show_reg(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int rc;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_reg)
+ rc = sprintf(buf, "%d\n", hd->ops->get_reg(hd));
+ else
+ rc = -ENXIO;
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_reg(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ int power = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->set_reg) {
+ hd->ops->set_reg(hd, power, power);
+ rc = count;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_show_reg_addr(struct device *dev, struct
device_attribute *attr,
+ char *buf)
+{
+ int rc;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_reg)
+ rc = sprintf(buf, "%d\n", hd->ops->get_reg(hd));
+ else
+ rc = -ENXIO;
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_reg_addr(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ int power = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->set_reg) {
+ hd->ops->set_reg(hd, power, power);
+ rc = count;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+#endif
+
+static ssize_t hdmi_show_ratio(struct device *dev, struct
device_attribute *attr,
+ char *buf)
+{
+ int rc;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ratio == RATIO_16V9)
+ rc = sprintf(buf, "%s\n", RATIO_16V9_STR);
+ else if (hd->ratio == RATIO_4V3)
+ rc = sprintf(buf, "%s\n", RATIO_4V3_STR);
+ else
+ rc = -ENXIO;
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_ratio(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (sysfs_streq(buf, RATIO_16V9_STR)) {
+ hd->ratio = RATIO_16V9;
+ } else if (sysfs_streq(buf, RATIO_4V3_STR)) {
+ hd->ratio = RATIO_4V3;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_show_power(struct device *dev, struct
device_attribute *attr,
+ char *buf)
+{
+ int rc;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_power) {
+ hd->ops->get_power(hd);
+ rc = sprintf(buf, "%d\n", hd->ops->get_power(hd));
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_power(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ int power = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->set_power) {
+ hdmi_dprintk(3, "hdmi: set power to %d\n", power);
+ hd->ops->set_power(hd, power);
+ rc = count;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc = -ENXIO;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_enable)
+ rc = sprintf(buf, "%d\n", hd->ops->get_enable(hd));
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ int enable = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ mutex_lock(&hd->update_lock);
+ hd->enable = enable;
+ if (hd->ops && hd->ops->set_enable) {
+ hdmi_dprintk(3, "hdmi: set enable to %d\n", enable);
+ hd->ops->set_enable(hd, enable);
+ rc = count;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_show_hotplug(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_hpd) {
+ rc = sprintf(buf, "%d\n", ! hd->ops->get_hpd(hd));
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static struct class *hdmi_class;
+
+static void hdmi_dev_release(struct device *dev)
+{
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ kfree(hd);
+}
+
+static struct device_attribute hdmi_dev_attributes[] = {
+ __ATTR(power, 0644, hdmi_show_power, hdmi_store_power),
+ __ATTR(ratio, 0644, hdmi_show_ratio, hdmi_store_ratio),
+#ifdef HDMI_DEBUG
+ __ATTR(reg_val, 0644, hdmi_show_reg, hdmi_store_reg),
+ __ATTR(reg_addr, 0644, hdmi_show_reg_addr, hdmi_store_reg_addr),
+#endif
+ __ATTR(enable, 0644, hdmi_show_enable, hdmi_store_enable),
+ __ATTR(hotplug, 0644, hdmi_show_hotplug, NULL),
+ __ATTR_NULL,
+};
+
+/**
+ * hdmi_dev_register - register a new object of hdmi_dev class.
+ * @name: the name of the new object(must be the same as the name of the
+ * respective framebuffer device).
+ * @devdata: an optional pointer to be stored in the device. The
+ * methods may retrieve it by using hdmi_get_data(hd).
+ * @ops: the hdmi operations structure.
+ *
+ * Creates and registers a new hdmi device. Returns either an ERR_PTR()
+ * or a pointer to the newly allocated device.
+ */
+int hdmi_dev_register(struct hdmi_dev *hd, struct device *parent,
+ void *devdata, struct hdmi_ops *ops, int fb_index)
+{
+ struct hdmi_event *evt_list;
+ int i;
+ int rc;
+
+ hd->dev = device_create(hdmi_class, parent,
+ MKDEV(HDMI_MAJOR, hdmidevno), hd, "hdmi%d", hdmidevno);
+ if (IS_ERR(hd->dev)) {
+ /* Not fatal */
+ hdmi_dprintk(1, "Unable to create device for hdmi %d; errno = %ld\n",
+ hdmidevno, PTR_ERR(hd->dev));
+ hd->dev = NULL;
+ }
+
+ spin_lock_init(&hd->lock);
+ mutex_init(&hd->hdmi_lock);
+ mutex_init(&hd->update_lock);
+
+ INIT_LIST_HEAD(&hd->hdmi_evt_list);
+ INIT_LIST_HEAD(&hd->free_evt_list);
+ hd->evt_ptr = evt_list =
+ kzalloc(MAX_HDMI_EVENT_SIZE * sizeof(struct hdmi_event), GFP_KERNEL);
+ for (i = 0; i < MAX_HDMI_EVENT_SIZE; i++) {
+ list_add(&evt_list->list, &hd->free_evt_list);
+ evt_list ++;
+ }
+
+ rc = hdmi_register_lcd(hd);
+ if (rc) {
+ device_unregister(hd->dev);
+ hdmi_dprintk(1, "Unable to create lcd device for hdmi\n");
+ goto dev_out;
+ }
+
+ hd->ops = ops;
+ hd->fb_info = registered_fb[fb_index];
+ if (! hd->fb_info) {
+ hdmi_dprintk(1, "Invalid associated fb\n");
+ goto dev_out;
+ }
+
+ /* Register CEC input device here */
+ /* Register audio Asoc device here */
+ /* Init hot plug uevent here */
+ /* Init HDCP here */
+
+ hd->thread = kthread_run(hdmi_event_thread, hd,
+ "hdmi%d", hdmidevno++);
+
+ if (IS_ERR(hd->thread)) {
+ rc = PTR_ERR(hd->thread);
+ hdmi_dprintk(1, "Unable to create internal thread for hdmi\n");
+ goto lcd_out;
+ }
+
+ return 0;
+
+ kthread_stop(hd->thread);
+lcd_out:
+ lcd_device_unregister(hd->lcd);
+dev_out:
+ device_unregister(hd->dev);
+ hdmi_dprintk(1, "Fail to register hdmi dev\n");
+ return rc;
+}
+EXPORT_SYMBOL(hdmi_dev_register);
+
+/**
+ * hdmi_dev_unregister - unregisters a object of hdmi_dev class.
+ * @hd: the hdmi device object to be unregistered and freed.
+ *
+ * Unregisters a previously registered via hdmi_dev_register object.
+ */
+void hdmi_dev_unregister(struct hdmi_dev *hd)
+{
+ if (!hd)
+ return;
+
+ mutex_lock(&hd->hdmi_lock);
+ hd->ops = NULL;
+ mutex_unlock(&hd->hdmi_lock);
+ lcd_device_unregister(hd->lcd);
+
+ kthread_stop(hd->thread);
+ device_unregister(hd->dev);
+}
+EXPORT_SYMBOL(hdmi_dev_unregister);
+
+static void __exit hdmi_class_exit(void)
+{
+ class_destroy(hdmi_class);
+}
+
+static int __init hdmi_class_init(void)
+{
+ hdmi_dprintk(1, "create hdmi class...\n");
+ hdmi_class = class_create(THIS_MODULE, "hdmi");
+ if (IS_ERR(hdmi_class)) {
+ hdmi_dprintk(1, "Unable to create hdmi class; errno = %ld\n",
+ PTR_ERR(hdmi_class));
+ return PTR_ERR(hdmi_class);
+ }
+
+ hdmi_class->dev_attrs = hdmi_dev_attributes;
+ hdmi_dprintk(1, "create hdmi class ok\n");
+ return 0;
+}
+
+subsys_initcall(hdmi_class_init);
+module_exit(hdmi_class_exit);
+
+module_param_named(debug, hdmi_core_debug, int, 0644);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/hdmi-mmp.c b/drivers/video/hdmi-mmp.c
new file mode 100644
index 0000000..f71f9fa
--- /dev/null
+++ b/drivers/video/hdmi-mmp.c
@@ -0,0 +1,445 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/freezer.h>
+#include <linux/gpio.h>
+#include <linux/hdmi.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/lcd.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <mach/mmp_hdmi.h>
+#include "hdmi-mmp.h"
+
+struct hdmi_mmp_dev {
+ struct hdmi_dev hdmi_dev;
+ struct clk *clk;
+ void *base;
+ void *sspa1_reg_base;
+ unsigned int gpio;
+ unsigned int irq;
+};
+
+static void hdmi_direct_write(void *base, unsigned addr, unsigned data)
+{
+ __raw_writel(data, base + BASE_OFFSET + addr);
+}
+
+static unsigned hdmi_direct_read(void *base, unsigned addr)
+{
+ return __raw_readl(base + BASE_OFFSET + addr);
+}
+
+static unsigned hdmi_read(void *base, unsigned addr)
+{
+ __raw_writel(addr | 1 << 30, base + BASE_OFFSET + HDMI_ADDR);
+ return __raw_readl((base + BASE_OFFSET + HDMI_DATA)) & 0xff;
+}
+
+static void hdmi_write(void *base, unsigned addr, unsigned data)
+{
+ __raw_writel(data & 0xff, base + BASE_OFFSET + HDMI_DATA);
+ __raw_writel(addr | 1 << 31, base + BASE_OFFSET + HDMI_ADDR);
+}
+
+static void hdmi_write_multi(struct hdmi_mmp_dev *md, u32 addr, u8 *buf,
+ u32 length)
+{
+ u32 i;
+ for (i = 0; i < length; i++)
+ hdmi_write(md->base, addr + i, *(buf + i));
+}
+
+static int hdmi_mmp_init_controller(struct hdmi_dev *dev)
+{
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+ u32 tmp;
+
+ printk(KERN_DEBUG "Init mmp hdmi ctroller...\n");
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 1<<27);
+
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_0, 0X00249b6d);
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_1, 0X0492eeee);
+ /* hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0X00003c30); */
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0X00003c10);
+ tmp = hdmi_direct_read(md->base, HDMI_PHY_CFG_3);
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_3, (tmp | 0X0000001));
+ msleep(50);
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_3, (tmp & (~0X0000001)));
+ /* temporatyly set the aduio cfg */
+ /* hdmi_direct_write(md->base, HDMI_AUDIO_CFG, 0x869); */
+
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL0, 0);
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL1, 0);
+
+ return 0;
+}
+
+static int hdmi_mmp_set_freq(struct hdmi_mmp_dev *md)
+{
+ int ret = 0;
+
+ /* Fix the pll2refdiv to 1(+2), to get 8.66MHz ref clk
+ * Stable val recomended between 8-12MHz. To get the reqd
+ * freq val, just program the fbdiv
+ * freq takes effect during a fc req
+ */
+ u32 residual;
+ volatile unsigned int temp = 0;
+ u32 freq;
+ unsigned int postdiv;
+ unsigned int freq_offset_inner;
+ unsigned int hdmi_pll_fbdiv;
+ unsigned int hdmi_pll_refdiv;
+ unsigned int kvco;
+ unsigned int freq_offset_inner_16;
+ unsigned int VPLL_CALCLK_DIV = 1;
+ unsigned int VDDM = 1;
+ unsigned int VDDL = 0x9;
+ unsigned int ICP = 0x9;
+ unsigned int VREG_IVREF = 2;
+ unsigned int INTPI = 5;
+ unsigned int VTH_VPLL_CA = 2;
+ int count = 0x10000;
+
+ ret = hdmi_get_freq ((struct hdmi_dev *)md, &freq);
+ if (ret)
+ return ret;
+
+ freq /= 1000;
+ /* round to freq = N*100k Hz */
+ residual = freq % 100;
+ freq = residual? (freq + 100 - residual) : freq;
+ freq /= 1000;
+
+ switch (freq) {
+ case 25:
+ postdiv = 0x3;
+ freq_offset_inner = 0x1C47;
+ freq_offset_inner_16 = 1;
+ hdmi_pll_fbdiv = 39;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 27:
+ postdiv = 0x3;
+ freq_offset_inner = 0x2d03;
+ freq_offset_inner_16 = 1;
+ hdmi_pll_fbdiv = 42;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 54:
+ postdiv = 0x2;
+ freq_offset_inner = 0x2d03;
+ freq_offset_inner_16 = 1;
+ hdmi_pll_fbdiv = 42;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 74:
+ postdiv = 0x2;
+ freq_offset_inner = 0x084B;
+ freq_offset_inner_16 = 0;
+ hdmi_pll_fbdiv = 57;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 108:
+ postdiv = 0x1;
+ freq_offset_inner = 0x2d03;
+ freq_offset_inner_16 = 1;
+ hdmi_pll_fbdiv = 42;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 148:
+ postdiv = 0x1;
+ freq_offset_inner = 0x084B;
+ freq_offset_inner_16 = 0;
+ hdmi_pll_fbdiv = 57;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ default:
+ printk(KERN_ERR "not supported freq %d\n", freq);
+ return -EINVAL;
+ }
+
+ hdmi_direct_write(md->base, HDMI_CLOCK_CFG, 1|1<<2|1<<4|4<<5|5<<9|5<<13);
+ //hdmi_direct_write(md->base, HDMI_CLOCK_CFG, 1|1<<2|4<<5|5<<9|5<<13);
+
+ /* power up the pll */
+ temp |= HDMI_PLL_CFG_3_HDMI_PLL_ON;
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+ temp = ((INTPI << HDMI_PLL_CFG_0_INTPI_BASE) |
+ (HDMI_PLL_CFG_0_CLK_DET_EN) |
+ (VDDM << HDMI_PLL_CFG_0_VDDM_BASE) |
+ (VDDL << HDMI_PLL_CFG_0_VDDL_BASE) |
+ (ICP << HDMI_PLL_CFG_0_ICP_BASE) |
+ (kvco << HDMI_PLL_CFG_0_KVCO_BASE) |
+ (VREG_IVREF << HDMI_PLL_CFG_0_VREG_IVREF_BASE) |
+ (VPLL_CALCLK_DIV << HDMI_PLL_CFG_0_VPLL_CALCLK_DIV_BASE) |
+ (postdiv << HDMI_PLL_CFG_0_POSTDIV_BASE) |
+ (ICP << HDMI_PLL_CFG_0_ICP_BASE));
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_0, temp);
+
+ temp = ((HDMI_PLL_CFG_1_MODE) |
+ (HDMI_PLL_CFG_1_EN_PANNEL)|
+ (HDMI_PLL_CFG_1_EN_HDMI)|
+ (VTH_VPLL_CA << HDMI_PLL_CFG_1_VTH_VPLL_CA_BASE)|
+ (freq_offset_inner << HDMI_PLL_CFG_1_FREQ_OFFSET_INNER_BASE)|
+ (freq_offset_inner_16 << 20));
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_1, temp);
+
+ temp = 0;
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_2, temp);
+
+ /* Power Down the pll and set the divider ratios*/
+ temp = (hdmi_pll_fbdiv << HDMI_PLL_CFG_3_HDMI_PLL_FBDIV_BASE)|
+ (hdmi_pll_refdiv << HDMI_PLL_CFG_3_HDMI_PLL_REFDIV_BASE);
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+ /* power up the pll */
+ temp |= HDMI_PLL_CFG_3_HDMI_PLL_ON;
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+ /*wait 10us : just an estimate*/
+ udelay(100);
+
+ /* release the pll out of reset*/
+ temp |= HDMI_PLL_CFG_3_HDMI_PLL_RSTB;
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+ /* PLL_LOCK should be 1, */
+ temp = hdmi_direct_read(md->base, HDMI_PLL_CFG_3);
+ temp &= 0x400000;
+ while ( !temp ) {
+ temp = hdmi_direct_read(md->base, HDMI_PLL_CFG_3);
+ temp &= 0x400000;
+ udelay(10);
+ count--;
+ if (count <= 0) {
+ printk(KERN_ERR "%s PLL lock error, PLL_CFG_3 %x\n",
+ __func__, temp);
+ return -EIO;
+ }
+ }
+ /* pll stablize in 10ms */
+ msleep(10);
+ return ret;
+}
+
+static irqreturn_t hdmi_mmp_hpd(int irq, void *data)
+{
+ printk(KERN_INFO "gpio hpd detected\n");
+
+ hdmi_event_signal((struct hdmi_dev *)data, HDMI_EV_PLUG);
+ return IRQ_HANDLED;
+}
+
+static int hdmi_mmp_get_hpd_status (struct hdmi_dev *dev)
+{
+ struct hdmi_mmp_dev *mh = (struct hdmi_mmp_dev *)dev;
+
+ return ! gpio_get_value(mh->gpio);
+}
+
+static int hdmi_send_packet (struct hdmi_mmp_dev *md, char *buf, int idx)
+{
+ u32 v;
+
+ hdmi_write_multi (md, HDTX_PKT0_BYTE0 + 0x20 * idx, buf, 32);
+
+ v = hdmi_read(md->base, HDTX_HST_PKT_CTRL0);
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL0, v | (1 << idx));
+ /* HW bug */
+ v &= ~(1 << idx);
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL0, v);
+ v = hdmi_read(md->base, HDTX_HST_PKT_CTRL1);
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL1, v | (1 << idx));
+}
+
+static int hdmi_mmp_get_reg (struct hdmi_dev *dev)
+{
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+ int i;
+
+ printk("************direct register*******************\n");
+ for (i = 0x8; i <= 0x30; i += 4)
+ printk("direct offset 0x%x is 0x%x\n", i, hdmi_direct_read(md->base, i));
+ printk("************indirect register*******************\n");
+ for (i = 0; i < 0x13e; i++)
+ printk("offset 0x%x is 0x%x\n", i, hdmi_read(md->base, i));
+}
+
+static int hdmi_mmp_set_reg (struct hdmi_dev *dev)
+{
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+
+ return 0;
+}
+
+static int hdmi_mmp_set_mode (struct hdmi_dev *dev, cea_mode_id id)
+{
+ char *buf;
+ u32 v;
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+
+ dev->mode_id = id;
+ hdmi_mmp_set_freq (md);
+ buf = kzalloc (32, GFP_KERNEL);
+
+ /* get proper avi packet */
+ hdmi_avi_info_frame (dev, buf);
+ /* write avi packet */
+ hdmi_send_packet (dev, buf, 0);
+ memset (buf, 0, 32);
+ hdmi_vender_info_frame (dev, buf);
+ hdmi_send_packet (dev, buf, 1);
+
+ v = hdmi_read(md->base, HDTX_HDMI_CTRL);
+ v &= ~0x78;
+ hdmi_write(md->base, HDTX_HDMI_CTRL, v | 0x1 | (dev->pixel_rept));
+ /* auto detect lcd timing */
+ hdmi_write(md->base, HDTX_VIDEO_CTRL, 0x58);
+ return 0;
+}
+
+static int hdmi_mmp_enable(struct hdmi_dev *dev, int enable)
+{
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+ u32 v;
+
+ v = hdmi_direct_read(md->base, HDMI_CLOCK_CFG);
+ v = enable ? (v | (1<<4)) : (v & ~(1<<4));
+ hdmi_direct_write(md->base, HDMI_CLOCK_CFG, v);
+
+ /* magic register */
+ hdmi_write(md->base, HDTX_TDATA3_1, 0x83);
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0x3c10);
+ //hdmi_direct_write(md->base,0x1c, 0xaa95);
+ return 0;
+}
+
+static struct hdmi_ops mmp_hdmi_ops = {
+ .init = hdmi_mmp_init_controller,
+ .get_hpd = hdmi_mmp_get_hpd_status,
+ .set_mode = hdmi_mmp_set_mode,
+ .set_enable = hdmi_mmp_enable,
+ .get_reg = hdmi_mmp_get_reg,
+ .set_reg = hdmi_mmp_set_reg,
+};
+
+static int __devinit hdmi_mmp_probe(struct platform_device *pdev)
+{
+ struct hdmi_mmp_mach_info *pdata;
+ struct hdmi_mmp_dev *mdev;
+ struct resource *res;
+ struct clk *clk;
+ int ret = -ENOMEM;
+
+ clk = clk_get(NULL, "HDMICLK");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get HDMICLK\n");
+ return PTR_ERR(clk);
+ }
+ clk_enable(clk);
+
+ mdev = kzalloc(sizeof(struct hdmi_mmp_dev), GFP_KERNEL);
+ if (!mdev)
+ goto out_mdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pdata = pdev->dev.platform_data;
+ if (res == NULL) {
+ printk(KERN_ERR "hdmi_probe: no memory resources given\n");
+ goto out_mdev;
+ }
+
+ mdev->base = ioremap(res->start, res->end - res->start + 1);
+ if (mdev->base == NULL) {
+ printk(KERN_ERR "%s: can't remap resgister area\n", __func__);
+ goto out_mdev;
+ }
+ mdev->gpio = pdata->gpio;
+ mdev->clk = clk;
+
+ ret = gpio_request(pdata->gpio, pdev->name);
+ if (ret < 0)
+ goto out_mdev;
+
+ ret = gpio_direction_input(pdata->gpio);
+ if (ret < 0)
+ goto out_free;
+
+ ret = request_irq(gpio_to_irq(pdata->gpio),
+ hdmi_mmp_hpd, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "HDMI HPD irq", mdev);
+ if(ret < 0)
+ goto free_gpio;
+
+ platform_set_drvdata(pdev, pdata);
+
+ ret = hdmi_dev_register((struct hdmi_dev*)mdev, &pdev->dev,
+ mdev, &mmp_hdmi_ops, pdata->fb_index);
+ if (ret) {
+ printk(KERN_ERR "mmp hdmi_dev_register fail\n");
+ goto irq_free;
+ }
+ return 0;
+
+irq_free:
+ free_irq(gpio_to_irq(pdata->gpio), NULL);
+free_gpio:
+ gpio_free(pdata->gpio);
+out_free:
+out_mdev:
+ kfree(mdev);
+ clk_disable(clk);
+ clk_put(clk);
+ return ret;
+}
+
+static struct platform_driver hdmi_mmp_driver = {
+ .driver = {
+ .name = "mmp-hdmi",
+ .owner = THIS_MODULE,
+ },
+ .probe = hdmi_mmp_probe,
+ //.remove = __devexit_p(hdmi_mmp_remove),
+};
+
+static int __devinit hdmi_mmp_init(void)
+{
+ return platform_driver_register(&hdmi_mmp_driver);
+}
+
+module_init(hdmi_mmp_init);
+
+MODULE_AUTHOR("Jun Nie <njun@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HDMI driver for Marvell MMP");
+MODULE_ALIAS("hdmi-mmp");
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index cb175fe..93f5b6e 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -292,62 +292,63 @@ static const struct fb_videomode modedb[] = {
};
#ifdef CONFIG_FB_MODE_HELPERS
-const struct fb_videomode cea_modes[64] = {
+const struct fb_videomode cea_modes[65] = {
/* #1: 640x480p@59.94/60Hz */
[1] = {
NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #3: 720x480p@59.94/60Hz */
[3] = {
NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #5: 1920x1080i@59.94/60Hz */
[5] = {
NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
},
/* #7: 720(1440)x480iH@59.94/60Hz */
[7] = {
NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
},
/* #9: 720(1440)x240pH@59.94/60Hz */
[9] = {
NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #18: 720x576pH@50Hz */
[18] = {
NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #19: 1280x720p@50Hz */
[19] = {
NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #20: 1920x1080i@50Hz */
[20] = {
NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
},
/* #32: 1920x1080p@23.98/24Hz */
[32] = {
NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #35: (2880)x480p4x@59.94/60Hz */
[35] = {
NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
};
+EXPORT_SYMBOL(cea_modes);
const struct fb_videomode vesa_modes[] = {
/* 0 640x350-85 VESA */
@@ -1119,6 +1120,24 @@ finished:
}
EXPORT_SYMBOL(fb_find_best_display);
+/**
+ * fb_find_cea_mode_index - find a cea video mode index
+ *
+ * RETURNS:
+ * cea mode index, 0 if none found
+ */
+const int fb_find_cea_mode_index(struct fb_videomode *m)
+{
+ int idx;
+
+ for (idx = 1; idx <= 64; idx ++) {
+ if (fb_mode_is_equal(&cea_modes[idx], m))
+ return idx;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(fb_find_cea_mode_index);
+
EXPORT_SYMBOL(fb_videomode_to_var);
EXPORT_SYMBOL(fb_var_to_videomode);
EXPORT_SYMBOL(fb_mode_is_equal);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 6a82748..14672f8 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1082,10 +1082,24 @@ extern void fb_bl_default_curve(struct fb_info
*fb_info, u8 off, u8 min, u8 max)
#define FB_MODE_IS_DETAILED 1
#define FB_MODE_IS_STANDARD 2
#define FB_MODE_IS_VESA 4
+#define FB_MODE_IS_CEA 5
#define FB_MODE_IS_CALCULATED 8
#define FB_MODE_IS_FIRST 16
#define FB_MODE_IS_FROM_VAR 32
+typedef enum {
+ CEA_640x480p60 = 1,
+ CEA_720x480p60 = 3,
+ CEA_1920x1080i60 = 5,
+ CEA_720x480i60 = 7,
+ CEA_720x240p60 = 9,
+ CEA_720x576p50 = 18,
+ CEA_1280x720p50 = 19,
+ CEA_1920x1080i50 = 20,
+ CEA_1920x1080p24 = 32,
+ CEA_2880x480p60 =35,
+} cea_mode_id;
+
extern int fbmon_dpms(const struct fb_info *fb_info);
extern int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
struct fb_info *info);
@@ -1124,6 +1138,7 @@ extern void fb_videomode_to_modelist(const
struct fb_videomode *modedb, int num,
struct list_head *head);
extern const struct fb_videomode *fb_find_best_display(const struct
fb_monspecs *specs,
struct list_head *head);
+extern const int fb_find_cea_mode_index(struct fb_videomode *m);
/* drivers/video/fbcmap.c */
extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
@@ -1155,7 +1170,7 @@ struct fb_videomode {
extern const char *fb_mode_option;
extern const struct fb_videomode vesa_modes[];
-extern const struct fb_videomode cea_modes[64];
+extern const struct fb_videomode cea_modes[65];
struct fb_modelist {
struct list_head list;
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
new file mode 100644
index 0000000..d8a3da9
--- /dev/null
+++ b/include/linux/hdmi.h
@@ -0,0 +1,164 @@
+/*
+ * HDMI Control Abstraction
+ *
+ */
+
+#ifndef _LINUX_HDMI_H
+#define _LINUX_HDMI_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+//#include <linux/notifier.h>
+#include <linux/fb.h>
+#include <linux/lcd.h>
+
+/* Notes on locking:
+ *
+ * hdmi_dev->ops_lock is an internal backlight lock protecting the ops
+ * field and no code outside the core should need to touch it.
+ *
+ * Access to set_power() is serialised by the update_lock mutex since
+ * most drivers seem to need this and historically get it wrong.
+ *
+ * Most drivers don't need locking on their get_power() method.
+ * If yours does, you need to implement it in the driver. You can use the
+ * update_lock mutex if appropriate.
+ *
+ * Any other use of the locks below is probably wrong.
+ */
+
+struct hdmi_dev;
+struct fb_info;
+
+struct hdmi_properties {
+ /* source or sink */
+ u16 type;
+};
+
+struct hdmi_ops {
+ int (*init)(struct hdmi_dev *);
+ int (*get_hpd)(struct hdmi_dev *);
+ int (*enable)(struct hdmi_dev *, int);
+ /* Get the LCD panel power status (0: full on, 1..3: controller
+ power on, flat panel power off, 4: full off), see FB_BLANK_XXX */
+ int (*get_power)(struct hdmi_dev *);
+ /* Enable or disable power to the LCD (0: on; 4: off, see FB_BLANK_XXX) */
+ int (*set_power)(struct hdmi_dev *, int power);
+ /* For debug */
+ int (*get_reg)(struct hdmi_dev *);
+ int (*set_reg)(struct hdmi_dev *, int offset, int v);
+ /* Get the current audio_cfg setting (0-max_audio_cfg) */
+ int (*get_audio_cfg)(struct hdmi_dev *);
+ /* Set LCD panel audio_cfg */
+ int (*set_audio_cfg)(struct hdmi_dev *, int audio_cfg);
+ int (*get_mode)(struct hdmi_dev *, struct fb_videomode *);
+ /* Set LCD panel mode (resolutions ...) */
+ int (*set_mode)(struct hdmi_dev *, cea_mode_id);
+ int (*get_enable)(struct hdmi_dev *);
+ int (*set_enable)(struct hdmi_dev *, int enable);
+ /* Check if given framebuffer device is the one LCD is bound to;
+ return 0 if not, !=0 if it is. If NULL, hdmi always matches the fb. */
+ int (*check_mode)(struct hdmi_dev *, struct fb_videomode *);
+};
+
+enum hdmi_event_type {
+ /* trigger gpio irq */
+ HDMI_EV_PLUG,
+ HDMI_EV_UNPLUG,
+ /* trigger in gpio irq or from fb change */
+ HDMI_EV_VIDEO,
+ /* trigger in gpio irq or from Asoc change */
+ HDMI_EV_AUDIO,
+ /* trigger from CEC/TV */
+ HDMI_EV_CEC,
+};
+
+struct hdmi_event {
+ enum hdmi_event_type type;
+ struct list_head list;
+};
+
+#define RATIO_16V9_STR "16:9"
+#define RATIO_4V3_STR "4:3"
+#define RATIO_16V9 (0)
+#define RATIO_4V3 (1)
+
+struct hdmi_dev {
+ struct device *dev;
+ struct hdmi_properties props;
+ /* This protects the 'ops' field. If 'ops' is NULL, the driver that
+ registered this device has been unloaded, and if class_get_devdata()
+ points to something in the body of that driver, it is also invalid. */
+ struct mutex hdmi_lock;
+ /* Serialise access to set_power method */
+ struct mutex update_lock;
+ spinlock_t lock; /* lock for claim and bus ops */
+ struct list_head hdmi_evt_list;
+ struct list_head free_evt_list;
+ struct task_struct *thread;
+ struct hdmi_event *evt_ptr;
+ /* If this is NULL, the backing module is unloaded */
+ struct hdmi_ops *ops;
+ /* The framebuffer notifier block */
+ //struct notifier_block fb_notif;
+ struct lcd_device *lcd;
+ struct fb_info *fb_info;
+ cea_mode_id mode_id;
+ //u32 deep_color;
+ u32 enable;
+ u32 pixel_rept;
+ u32 ratio;
+};
+
+struct hdmi_platform_data {
+ /* reset hdmi panel device. */
+ int (*reset)(struct hdmi_dev *ld);
+ /* on or off to hdmi panel. if 'enable' is 0 then
+ hdmi power off and 1, hdmi power on. */
+ int (*power_on)(struct hdmi_dev *ld, int enable);
+
+ /* it indicates whether hdmi panel was enabled
+ from bootloader or not. */
+ int hdmi_enabled;
+ /* video, audio, cec, hdcp */
+ unsigned int features;
+ /* it means delay for stable time when it becomes low to high
+ or high to low that is dependent on whether reset gpio is
+ low active or high active. */
+ unsigned int reset_delay;
+ /* stable time needing to become hdmi power on. */
+ unsigned int power_on_delay;
+ /* stable time needing to become hdmi power off. */
+ unsigned int power_off_delay;
+
+ /* it could be used for any purpose. */
+ void *pdata;
+};
+
+static inline void hdmi_set_power(struct hdmi_dev *ld, int power)
+{
+ mutex_lock(&ld->update_lock);
+ if (ld->ops && ld->ops->set_power)
+ ld->ops->set_power(ld, power);
+ mutex_unlock(&ld->update_lock);
+}
+
+extern int hdmi_dev_register(struct hdmi_dev *hdmi_dev,
+ struct device *parent, void *devdata, struct hdmi_ops *ops,
+ int fb_index);
+extern void hdmi_dev_unregister(struct hdmi_dev *ld);
+
+#define to_hdmi_dev(obj) container_of(obj, struct hdmi_dev, dev)
+
+static inline void * hdmi_get_data(struct hdmi_dev *ld_dev)
+{
+ return dev_get_drvdata(ld_dev->dev);
+}
+
+
+#endif
+
+int hdmi_get_freq (struct hdmi_dev *hd, u32 *need_freq);
+int hdmi_vender_info_frame (struct hdmi_dev *hd, char buf[]);
+int hdmi_avi_info_frame (struct hdmi_dev *hd, char buf[]);
+void hdmi_event_signal(struct hdmi_dev *dev, enum hdmi_event_type type);
diff --git a/include/linux/lcd.h b/include/linux/lcd.h
index 8877123..b09e0f9 100644
--- a/include/linux/lcd.h
+++ b/include/linux/lcd.h
@@ -48,6 +48,8 @@ struct lcd_ops {
int (*set_contrast)(struct lcd_device *, int contrast);
/* Set LCD panel mode (resolutions ...) */
int (*set_mode)(struct lcd_device *, struct fb_videomode *);
+ /* Get LCD panel mode (resolutions ...) */
+ int (*get_mode)(struct lcd_device *, struct fb_videomode *);
/* Check if given framebuffer device is the one LCD is bound to;
return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */
int (*check_fb)(struct lcd_device *, struct fb_info *);
diff --git a/include/linux/major.h b/include/linux/major.h
index 6a8ca98..e6e46e2 100644
--- a/include/linux/major.h
+++ b/include/linux/major.h
@@ -170,6 +170,7 @@
#define IBM_FS3270_MAJOR 228
#define VIOTAPE_MAJOR 230
+#define HDMI_MAJOR 231
#define BLOCK_EXT_MAJOR 259
#define SCSI_OSD_MAJOR 260 /* open-osd's OSD scsi device */
[-- Attachment #2: hdmi_draft.diff --]
[-- Type: text/x-patch, Size: 40666 bytes --]
diff --git a/drivers/video/hdmi-mmp.c b/drivers/video/hdmi-mmp.c
new file mode 100644
index 0000000..f71f9fa
--- /dev/null
+++ b/drivers/video/hdmi-mmp.c
@@ -0,0 +1,445 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/freezer.h>
+#include <linux/gpio.h>
+#include <linux/hdmi.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/lcd.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <mach/mmp_hdmi.h>
+#include "hdmi-mmp.h"
+
+struct hdmi_mmp_dev {
+ struct hdmi_dev hdmi_dev;
+ struct clk *clk;
+ void *base;
+ void *sspa1_reg_base;
+ unsigned int gpio;
+ unsigned int irq;
+};
+
+static void hdmi_direct_write(void *base, unsigned addr, unsigned data)
+{
+ __raw_writel(data, base + BASE_OFFSET + addr);
+}
+
+static unsigned hdmi_direct_read(void *base, unsigned addr)
+{
+ return __raw_readl(base + BASE_OFFSET + addr);
+}
+
+static unsigned hdmi_read(void *base, unsigned addr)
+{
+ __raw_writel(addr | 1 << 30, base + BASE_OFFSET + HDMI_ADDR);
+ return __raw_readl((base + BASE_OFFSET + HDMI_DATA)) & 0xff;
+}
+
+static void hdmi_write(void *base, unsigned addr, unsigned data)
+{
+ __raw_writel(data & 0xff, base + BASE_OFFSET + HDMI_DATA);
+ __raw_writel(addr | 1 << 31, base + BASE_OFFSET + HDMI_ADDR);
+}
+
+static void hdmi_write_multi(struct hdmi_mmp_dev *md, u32 addr, u8 *buf,
+ u32 length)
+{
+ u32 i;
+ for (i = 0; i < length; i++)
+ hdmi_write(md->base, addr + i, *(buf + i));
+}
+
+static int hdmi_mmp_init_controller(struct hdmi_dev *dev)
+{
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+ u32 tmp;
+
+ printk(KERN_DEBUG "Init mmp hdmi ctroller...\n");
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 1<<27);
+
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_0, 0X00249b6d);
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_1, 0X0492eeee);
+ /* hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0X00003c30); */
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0X00003c10);
+ tmp = hdmi_direct_read(md->base, HDMI_PHY_CFG_3);
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_3, (tmp | 0X0000001));
+ msleep(50);
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_3, (tmp & (~0X0000001)));
+ /* temporatyly set the aduio cfg */
+ /* hdmi_direct_write(md->base, HDMI_AUDIO_CFG, 0x869); */
+
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL0, 0);
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL1, 0);
+
+ return 0;
+}
+
+static int hdmi_mmp_set_freq(struct hdmi_mmp_dev *md)
+{
+ int ret = 0;
+
+ /* Fix the pll2refdiv to 1(+2), to get 8.66MHz ref clk
+ * Stable val recomended between 8-12MHz. To get the reqd
+ * freq val, just program the fbdiv
+ * freq takes effect during a fc req
+ */
+ u32 residual;
+ volatile unsigned int temp = 0;
+ u32 freq;
+ unsigned int postdiv;
+ unsigned int freq_offset_inner;
+ unsigned int hdmi_pll_fbdiv;
+ unsigned int hdmi_pll_refdiv;
+ unsigned int kvco;
+ unsigned int freq_offset_inner_16;
+ unsigned int VPLL_CALCLK_DIV = 1;
+ unsigned int VDDM = 1;
+ unsigned int VDDL = 0x9;
+ unsigned int ICP = 0x9;
+ unsigned int VREG_IVREF = 2;
+ unsigned int INTPI = 5;
+ unsigned int VTH_VPLL_CA = 2;
+ int count = 0x10000;
+
+ ret = hdmi_get_freq ((struct hdmi_dev *)md, &freq);
+ if (ret)
+ return ret;
+
+ freq /= 1000;
+ /* round to freq = N*100k Hz */
+ residual = freq % 100;
+ freq = residual? (freq + 100 - residual) : freq;
+ freq /= 1000;
+
+ switch (freq) {
+ case 25:
+ postdiv = 0x3;
+ freq_offset_inner = 0x1C47;
+ freq_offset_inner_16 = 1;
+ hdmi_pll_fbdiv = 39;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 27:
+ postdiv = 0x3;
+ freq_offset_inner = 0x2d03;
+ freq_offset_inner_16 = 1;
+ hdmi_pll_fbdiv = 42;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 54:
+ postdiv = 0x2;
+ freq_offset_inner = 0x2d03;
+ freq_offset_inner_16 = 1;
+ hdmi_pll_fbdiv = 42;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 74:
+ postdiv = 0x2;
+ freq_offset_inner = 0x084B;
+ freq_offset_inner_16 = 0;
+ hdmi_pll_fbdiv = 57;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 108:
+ postdiv = 0x1;
+ freq_offset_inner = 0x2d03;
+ freq_offset_inner_16 = 1;
+ hdmi_pll_fbdiv = 42;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ case 148:
+ postdiv = 0x1;
+ freq_offset_inner = 0x084B;
+ freq_offset_inner_16 = 0;
+ hdmi_pll_fbdiv = 57;
+ hdmi_pll_refdiv = 0;
+ kvco = 0x4;
+ break;
+ default:
+ printk(KERN_ERR "not supported freq %d\n", freq);
+ return -EINVAL;
+ }
+
+ hdmi_direct_write(md->base, HDMI_CLOCK_CFG, 1|1<<2|1<<4|4<<5|5<<9|5<<13);
+ //hdmi_direct_write(md->base, HDMI_CLOCK_CFG, 1|1<<2|4<<5|5<<9|5<<13);
+
+ /* power up the pll */
+ temp |= HDMI_PLL_CFG_3_HDMI_PLL_ON;
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+ temp = ((INTPI << HDMI_PLL_CFG_0_INTPI_BASE) |
+ (HDMI_PLL_CFG_0_CLK_DET_EN) |
+ (VDDM << HDMI_PLL_CFG_0_VDDM_BASE) |
+ (VDDL << HDMI_PLL_CFG_0_VDDL_BASE) |
+ (ICP << HDMI_PLL_CFG_0_ICP_BASE) |
+ (kvco << HDMI_PLL_CFG_0_KVCO_BASE) |
+ (VREG_IVREF << HDMI_PLL_CFG_0_VREG_IVREF_BASE) |
+ (VPLL_CALCLK_DIV << HDMI_PLL_CFG_0_VPLL_CALCLK_DIV_BASE) |
+ (postdiv << HDMI_PLL_CFG_0_POSTDIV_BASE) |
+ (ICP << HDMI_PLL_CFG_0_ICP_BASE));
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_0, temp);
+
+ temp = ((HDMI_PLL_CFG_1_MODE) |
+ (HDMI_PLL_CFG_1_EN_PANNEL)|
+ (HDMI_PLL_CFG_1_EN_HDMI)|
+ (VTH_VPLL_CA << HDMI_PLL_CFG_1_VTH_VPLL_CA_BASE)|
+ (freq_offset_inner << HDMI_PLL_CFG_1_FREQ_OFFSET_INNER_BASE)|
+ (freq_offset_inner_16 << 20));
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_1, temp);
+
+ temp = 0;
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_2, temp);
+
+ /* Power Down the pll and set the divider ratios*/
+ temp = (hdmi_pll_fbdiv << HDMI_PLL_CFG_3_HDMI_PLL_FBDIV_BASE)|
+ (hdmi_pll_refdiv << HDMI_PLL_CFG_3_HDMI_PLL_REFDIV_BASE);
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+ /* power up the pll */
+ temp |= HDMI_PLL_CFG_3_HDMI_PLL_ON;
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+ /*wait 10us : just an estimate*/
+ udelay(100);
+
+ /* release the pll out of reset*/
+ temp |= HDMI_PLL_CFG_3_HDMI_PLL_RSTB;
+ hdmi_direct_write(md->base, HDMI_PLL_CFG_3, temp);
+
+ /* PLL_LOCK should be 1, */
+ temp = hdmi_direct_read(md->base, HDMI_PLL_CFG_3);
+ temp &= 0x400000;
+ while ( !temp ) {
+ temp = hdmi_direct_read(md->base, HDMI_PLL_CFG_3);
+ temp &= 0x400000;
+ udelay(10);
+ count--;
+ if (count <= 0) {
+ printk(KERN_ERR "%s PLL lock error, PLL_CFG_3 %x\n",
+ __func__, temp);
+ return -EIO;
+ }
+ }
+ /* pll stablize in 10ms */
+ msleep(10);
+ return ret;
+}
+
+static irqreturn_t hdmi_mmp_hpd(int irq, void *data)
+{
+ printk(KERN_INFO "gpio hpd detected\n");
+
+ hdmi_event_signal((struct hdmi_dev *)data, HDMI_EV_PLUG);
+ return IRQ_HANDLED;
+}
+
+static int hdmi_mmp_get_hpd_status (struct hdmi_dev *dev)
+{
+ struct hdmi_mmp_dev *mh = (struct hdmi_mmp_dev *)dev;
+
+ return ! gpio_get_value(mh->gpio);
+}
+
+static int hdmi_send_packet (struct hdmi_mmp_dev *md, char *buf, int idx)
+{
+ u32 v;
+
+ hdmi_write_multi (md, HDTX_PKT0_BYTE0 + 0x20 * idx, buf, 32);
+
+ v = hdmi_read(md->base, HDTX_HST_PKT_CTRL0);
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL0, v | (1 << idx));
+ /* HW bug */
+ v &= ~(1 << idx);
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL0, v);
+ v = hdmi_read(md->base, HDTX_HST_PKT_CTRL1);
+ hdmi_write(md->base, HDTX_HST_PKT_CTRL1, v | (1 << idx));
+}
+
+static int hdmi_mmp_get_reg (struct hdmi_dev *dev)
+{
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+ int i;
+
+ printk("************direct register*******************\n");
+ for (i = 0x8; i <= 0x30; i += 4)
+ printk("direct offset 0x%x is 0x%x\n", i, hdmi_direct_read(md->base, i));
+ printk("************indirect register*******************\n");
+ for (i = 0; i < 0x13e; i++)
+ printk("offset 0x%x is 0x%x\n", i, hdmi_read(md->base, i));
+}
+
+static int hdmi_mmp_set_reg (struct hdmi_dev *dev)
+{
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+
+ return 0;
+}
+
+static int hdmi_mmp_set_mode (struct hdmi_dev *dev, cea_mode_id id)
+{
+ char *buf;
+ u32 v;
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+
+ dev->mode_id = id;
+ hdmi_mmp_set_freq (md);
+ buf = kzalloc (32, GFP_KERNEL);
+
+ /* get proper avi packet */
+ hdmi_avi_info_frame (dev, buf);
+ /* write avi packet */
+ hdmi_send_packet (dev, buf, 0);
+ memset (buf, 0, 32);
+ hdmi_vender_info_frame (dev, buf);
+ hdmi_send_packet (dev, buf, 1);
+
+ v = hdmi_read(md->base, HDTX_HDMI_CTRL);
+ v &= ~0x78;
+ hdmi_write(md->base, HDTX_HDMI_CTRL, v | 0x1 | (dev->pixel_rept));
+ /* auto detect lcd timing */
+ hdmi_write(md->base, HDTX_VIDEO_CTRL, 0x58);
+ return 0;
+}
+
+static int hdmi_mmp_enable(struct hdmi_dev *dev, int enable)
+{
+ struct hdmi_mmp_dev *md = (struct hdmi_mmp_dev *)dev;
+ u32 v;
+
+ v = hdmi_direct_read(md->base, HDMI_CLOCK_CFG);
+ v = enable ? (v | (1<<4)) : (v & ~(1<<4));
+ hdmi_direct_write(md->base, HDMI_CLOCK_CFG, v);
+
+ /* magic register */
+ hdmi_write(md->base, HDTX_TDATA3_1, 0x83);
+ hdmi_direct_write(md->base, HDMI_PHY_CFG_2, 0x3c10);
+ //hdmi_direct_write(md->base,0x1c, 0xaa95);
+ return 0;
+}
+
+static struct hdmi_ops mmp_hdmi_ops = {
+ .init = hdmi_mmp_init_controller,
+ .get_hpd = hdmi_mmp_get_hpd_status,
+ .set_mode = hdmi_mmp_set_mode,
+ .set_enable = hdmi_mmp_enable,
+ .get_reg = hdmi_mmp_get_reg,
+ .set_reg = hdmi_mmp_set_reg,
+};
+
+static int __devinit hdmi_mmp_probe(struct platform_device *pdev)
+{
+ struct hdmi_mmp_mach_info *pdata;
+ struct hdmi_mmp_dev *mdev;
+ struct resource *res;
+ struct clk *clk;
+ int ret = -ENOMEM;
+
+ clk = clk_get(NULL, "HDMICLK");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get HDMICLK\n");
+ return PTR_ERR(clk);
+ }
+ clk_enable(clk);
+
+ mdev = kzalloc(sizeof(struct hdmi_mmp_dev), GFP_KERNEL);
+ if (!mdev)
+ goto out_mdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pdata = pdev->dev.platform_data;
+ if (res == NULL) {
+ printk(KERN_ERR "hdmi_probe: no memory resources given\n");
+ goto out_mdev;
+ }
+
+ mdev->base = ioremap(res->start, res->end - res->start + 1);
+ if (mdev->base == NULL) {
+ printk(KERN_ERR "%s: can't remap resgister area\n", __func__);
+ goto out_mdev;
+ }
+ mdev->gpio = pdata->gpio;
+ mdev->clk = clk;
+
+ ret = gpio_request(pdata->gpio, pdev->name);
+ if (ret < 0)
+ goto out_mdev;
+
+ ret = gpio_direction_input(pdata->gpio);
+ if (ret < 0)
+ goto out_free;
+
+ ret = request_irq(gpio_to_irq(pdata->gpio),
+ hdmi_mmp_hpd, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "HDMI HPD irq", mdev);
+ if(ret < 0)
+ goto free_gpio;
+
+ platform_set_drvdata(pdev, pdata);
+
+ ret = hdmi_dev_register((struct hdmi_dev*)mdev, &pdev->dev,
+ mdev, &mmp_hdmi_ops, pdata->fb_index);
+ if (ret) {
+ printk(KERN_ERR "mmp hdmi_dev_register fail\n");
+ goto irq_free;
+ }
+ return 0;
+
+irq_free:
+ free_irq(gpio_to_irq(pdata->gpio), NULL);
+free_gpio:
+ gpio_free(pdata->gpio);
+out_free:
+out_mdev:
+ kfree(mdev);
+ clk_disable(clk);
+ clk_put(clk);
+ return ret;
+}
+
+static struct platform_driver hdmi_mmp_driver = {
+ .driver = {
+ .name = "mmp-hdmi",
+ .owner = THIS_MODULE,
+ },
+ .probe = hdmi_mmp_probe,
+ //.remove = __devexit_p(hdmi_mmp_remove),
+};
+
+static int __devinit hdmi_mmp_init(void)
+{
+ return platform_driver_register(&hdmi_mmp_driver);
+}
+
+module_init(hdmi_mmp_init);
+
+MODULE_AUTHOR("Jun Nie <njun@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HDMI driver for Marvell MMP");
+MODULE_ALIAS("hdmi-mmp");
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
new file mode 100644
index 0000000..c845dc7
--- /dev/null
+++ b/drivers/video/hdmi.c
@@ -0,0 +1,675 @@
+/*
+ * HDMI Control Abstraction
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/freezer.h>
+#include <linux/hdmi.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/lcd.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define HDMI_DEBUG
+static int hdmi_core_debug = 5;
+#define hdmi_dprintk(level, fmt, arg...) if (hdmi_core_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt , __func__, ## arg)
+
+/* Define the max number of events to buffer */
+#define MAX_HDMI_EVENT_SIZE 16
+
+static int hdmidevno;
+
+/* ==================== info frame utility ===================== */
+
+int hdmi_vender_info_frame (struct hdmi_dev *hd, char buf[])
+{
+ buf[0] = 0x84;
+ buf[1] = 0x1;
+ buf[2] = 0xa;
+ buf[3] = 0x70;
+ buf[4] = 0x1;
+}
+EXPORT_SYMBOL_GPL(hdmi_vender_info_frame);
+
+int hdmi_avi_info_frame (struct hdmi_dev *hd, char buf[])
+{
+ int count;
+ char check_sum;
+
+ hdmi_dprintk(3, "pack avi infoframe\n");
+ /* packet header for AVI Packet in pkt1 */
+ /* Fix me. we should set bits according to format */
+ buf[0] = 0x82; /* InfoFrame Type, AVI frame */
+ buf[1] = 0x2; /* Version = 02 */
+ buf[2] = 0xd; /* Length = 13 */
+ buf[3] = 0x0; /* checksum */
+ buf[4] = 0x0; /* Fix to RGB format currently */
+ /*
+ buf[4] = 0x10; // RGB, Active Format(r0,r1,r2,r3) valid, no bar, no scan
+ buf[4] = 0x30; // HDMI_YUV422, Active Format(r0,r1,r2,r3) valid, no bar, no scan
+ buf[4] = 0x50; // HDMI_YUV444, Active Format(r0,r1,r2,r3) valid, no bar, no scan
+ */
+
+ buf[5] = 0xa8; /* ITU-R 709, 16:9 */
+ buf[6] = 0x20;
+ /* buf[6] = 0x0; // No IT content, xvYCC601, no scaling */
+ buf[7] = hd->mode_id;
+ /* limited YCC range, graphics, no repeat Pixel repitition factor */
+ buf[8] = hd->pixel_rept;
+
+ buf[9] = 0x0;
+ buf[10] = 0x0;
+ buf[11] = 0x0;
+ buf[12] = 0x0;
+ buf[13] = 0x0;
+ check_sum = 0;
+ for (count = 0; count < 14; count++)
+ check_sum += buf[count];
+ buf[3] = 0x100 - check_sum;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_avi_info_frame);
+
+int hdmi_get_freq (struct hdmi_dev *hd, u32 *need_freq)
+{
+ u64 div_result;
+ int freq;
+
+ /* Calc divider according to pclk in pico second */
+ div_result = 1000000000000ll;
+ do_div(div_result, cea_modes[hd->mode_id].pixclock);
+ freq = (u32)div_result;
+
+ hdmi_dprintk(3, "get base pclk %d\n", freq);
+
+ if (FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode)
+ freq >>= 1;
+
+ if (36 == hd->fb_info->var.bits_per_pixel)
+ freq += freq >> 1;
+ else if (30 == hd->fb_info->var.bits_per_pixel)
+ freq += freq >> 2;
+
+ /* We only support repeat=2/4 currently. Add more later */
+ if (!(hd->pixel_rept == 0 || hd->pixel_rept == 1
+ || hd->pixel_rept == 3)) {
+ hdmi_dprintk(3, "Not support pix repeat %d\n", hd->pixel_rept);
+ return -EINVAL;
+ }
+
+ if (1 == hd->pixel_rept)
+ {
+ if (!(FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode))
+ freq <<= 1;
+ } else if (3 == hd->pixel_rept) {
+ if (!(FB_VMODE_INTERLACED & cea_modes[hd->mode_id].vmode))
+ freq <<= 2;
+ else
+ freq <<= 1;
+ }
+
+ hdmi_dprintk(3, "get hdmi pclk %d\n", freq);
+ *need_freq = freq;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hdmi_get_freq);
+
+/* ==================== video part ===================== */
+int hdmi_lcd_check_fb(struct lcd_device * ld, struct fb_info *fi)
+{
+ struct hdmi_dev *hd = lcd_get_data(ld);
+
+ hdmi_dprintk(3, "hdmi_lcd_check_fb..\n");
+ return 1;
+}
+
+/* We'd better update mode list in hot plug operation.
+ LCD framework does not implement get_mode yet */
+static int hdmi_lcd_get_mode(struct lcd_device *ld, struct fb_videomode *mode)
+{
+ struct hdmi_dev *hd = lcd_get_data(ld);
+
+ /* TODO: return parsed mode list */
+ return 0;
+}
+
+static int hdmi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *mode)
+{
+ //struct hdmi_lcd_data *data = lcd_get_data(lcd);
+ struct hdmi_dev *hd = lcd_get_data(ld);
+ cea_mode_id mode_id;
+
+ hdmi_dprintk(3, "hdmi_lcd_set_mode dev\n");
+ mode_id = fb_find_cea_mode_index(mode);
+ if (!mode_id) {
+ hdmi_dprintk(1, "unknown video mode %d\n", mode_id);
+ return -EINVAL;
+ }
+ /* If high level on detection signal, no connection */
+ if(! hd->ops->get_hpd(hd))
+ return 0;
+
+ hd->ops->set_mode(hd, mode_id);
+
+ if (hd->ops && hd->ops->set_enable)
+ hd->ops->set_enable(hd, 1);
+}
+
+static struct lcd_ops hdmi_lcd_ops = {
+ .get_mode = hdmi_lcd_get_mode,
+ .set_mode = hdmi_lcd_set_mode,
+ .check_fb = hdmi_lcd_check_fb,
+};
+
+static int hdmi_register_lcd(struct hdmi_dev *dev)
+{
+ int ret = 0;
+
+ hdmi_dprintk(3, "registering lcd device...\n");
+ /* FIX ME! How to hook expected fb if more than one fb exist? */
+ dev->lcd = lcd_device_register("hdmi-lcd", dev->dev, dev,
+ &hdmi_lcd_ops);
+
+ if (IS_ERR(dev->lcd)) {
+ ret = PTR_ERR(dev->lcd);
+ dev->lcd = NULL;
+ return ret;
+ }
+ hdmi_dprintk(3, "registering lcd device ok\n");
+ return 0;
+}
+
+/* ==================== audio part ===================== */
+/* ==================== cec part ===================== */
+/* ==================== edid part ===================== */
+
+/* ==================== hot plug part ===================== */
+static int hdmi_hotplug(struct hdmi_dev *hd)
+{
+ int ret;
+
+ msleep(100);
+ ret = hd->ops->get_hpd(hd);
+ if(!ret) {
+ hdmi_dprintk(3, "uplugged. dev %p\n", hd);
+ ret = hd->ops->set_enable(hd, 0);
+ lock_fb_info(hd->fb_info);
+ /* We should clear mode list here */
+ /* ... */
+ unlock_fb_info(hd->fb_info);
+ return 0;
+ }
+
+ ret = hd->ops->init(hd);
+ if (ret) {
+ hdmi_dprintk(1, "init fail\n");
+ }
+
+ if(hd->fb_info && hd->fb_info->mode) {
+ lock_fb_info(hd->fb_info);
+ /* Fix me! We should get EDID and parse mode list here */
+ /* But now we use fix mode list first before EDID util is ready */
+ /* ... */
+ unlock_fb_info(hd->fb_info);
+
+ ret = hdmi_lcd_set_mode(hd->lcd, hd->fb_info->mode);
+ if (ret) {
+ hdmi_dprintk(1, "fail to set video mode\n");
+ }
+ } else {
+ hdmi_dprintk(3, "no avaliable video mode\n");
+ }
+
+ if (hd->enable)
+ ret = hd->ops->set_enable(hd, 1);
+ return ret;
+}
+
+/* Handling hot plug HDCP and CEC */
+static int hdmi_event_thread(void *data)
+{
+ struct hdmi_dev *dev = (struct hdmi_dev *)data;
+ struct hdmi_event *evt;
+
+ while (!kthread_should_stop()) {
+
+ spin_lock_irq(&dev->lock);
+ if (list_empty(&dev->hdmi_evt_list)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(&dev->lock);
+ schedule();
+ continue;
+ }
+
+ evt = list_entry(dev->hdmi_evt_list.next, typeof(*evt), list);
+ list_del(&evt->list);
+ spin_unlock_irq(&dev->lock);
+ hdmi_dprintk(1, "Receive evt type %d\n", evt->type);
+
+ mutex_lock(&dev->hdmi_lock);
+ switch (evt->type) {
+ /* trigger gpio irq */
+ case HDMI_EV_PLUG:
+ hdmi_hotplug(dev);
+ break;
+ /* trigger from TV/CEC */
+ case HDMI_EV_CEC:
+ break;
+ default:
+ hdmi_dprintk(1, "Invalid event type %d\n", evt->type);
+ break;
+ }
+ mutex_unlock(&dev->hdmi_lock);
+ }
+
+ return 0;
+}
+
+/**
+ * hdmi_event_signal() - schedules the hdmi event thread
+ * @dev: the struct hdmi_dev device
+ *
+ * This routine will tell hdmi-core to start decoding stored ir data.
+ */
+void hdmi_event_signal(struct hdmi_dev *dev, enum hdmi_event_type type)
+{
+ unsigned long flags;
+ struct hdmi_event *evt;
+
+ hdmi_dprintk(1, "hdmi event %d stored, dev %p\n", type, dev);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if(HDMI_EV_PLUG == type) {
+ list_for_each_entry(evt, &dev->hdmi_evt_list, list) {
+ if (HDMI_EV_PLUG == evt->type) {
+ hdmi_dprintk(1, "pending hotplug event, ignore new ones\n");
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return;
+ }
+ }
+ }
+ evt = list_entry(dev->free_evt_list.next, struct hdmi_event, list);
+ evt->type = type;
+ list_move_tail(&evt->list, &dev->hdmi_evt_list);
+ wake_up_process(dev->thread);
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+EXPORT_SYMBOL_GPL(hdmi_event_signal);
+
+/* ==================== sys fs part ===================== */
+#ifdef HDMI_DEBUG
+static ssize_t hdmi_show_reg(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int rc;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_reg)
+ rc = sprintf(buf, "%d\n", hd->ops->get_reg(hd));
+ else
+ rc = -ENXIO;
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_reg(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ int power = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->set_reg) {
+ hd->ops->set_reg(hd, power, power);
+ rc = count;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_show_reg_addr(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int rc;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_reg)
+ rc = sprintf(buf, "%d\n", hd->ops->get_reg(hd));
+ else
+ rc = -ENXIO;
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_reg_addr(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ int power = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->set_reg) {
+ hd->ops->set_reg(hd, power, power);
+ rc = count;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+#endif
+
+static ssize_t hdmi_show_ratio(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int rc;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ratio == RATIO_16V9)
+ rc = sprintf(buf, "%s\n", RATIO_16V9_STR);
+ else if (hd->ratio == RATIO_4V3)
+ rc = sprintf(buf, "%s\n", RATIO_4V3_STR);
+ else
+ rc = -ENXIO;
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_ratio(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (sysfs_streq(buf, RATIO_16V9_STR)) {
+ hd->ratio = RATIO_16V9;
+ } else if (sysfs_streq(buf, RATIO_4V3_STR)) {
+ hd->ratio = RATIO_4V3;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_show_power(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ int rc;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_power) {
+ hd->ops->get_power(hd);
+ rc = sprintf(buf, "%d\n", hd->ops->get_power(hd));
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_power(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ int power = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->set_power) {
+ hdmi_dprintk(3, "hdmi: set power to %d\n", power);
+ hd->ops->set_power(hd, power);
+ rc = count;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_show_enable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc = -ENXIO;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_enable)
+ rc = sprintf(buf, "%d\n", hd->ops->get_enable(hd));
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_store_enable(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ int enable = simple_strtoul(buf, &endp, 0);
+ size_t size = endp - buf;
+
+ if (isspace(*endp))
+ size++;
+ if (size != count)
+ return -EINVAL;
+
+ mutex_lock(&hd->update_lock);
+ hd->enable = enable;
+ if (hd->ops && hd->ops->set_enable) {
+ hdmi_dprintk(3, "hdmi: set enable to %d\n", enable);
+ hd->ops->set_enable(hd, enable);
+ rc = count;
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static ssize_t hdmi_show_hotplug(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc = -ENXIO;
+ char *endp;
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+
+ mutex_lock(&hd->update_lock);
+ if (hd->ops && hd->ops->get_hpd) {
+ rc = sprintf(buf, "%d\n", ! hd->ops->get_hpd(hd));
+ }
+ mutex_unlock(&hd->update_lock);
+
+ return rc;
+}
+
+static struct class *hdmi_class;
+
+static void hdmi_dev_release(struct device *dev)
+{
+ struct hdmi_dev *hd = dev_get_drvdata(dev);
+ kfree(hd);
+}
+
+static struct device_attribute hdmi_dev_attributes[] = {
+ __ATTR(power, 0644, hdmi_show_power, hdmi_store_power),
+ __ATTR(ratio, 0644, hdmi_show_ratio, hdmi_store_ratio),
+#ifdef HDMI_DEBUG
+ __ATTR(reg_val, 0644, hdmi_show_reg, hdmi_store_reg),
+ __ATTR(reg_addr, 0644, hdmi_show_reg_addr, hdmi_store_reg_addr),
+#endif
+ __ATTR(enable, 0644, hdmi_show_enable, hdmi_store_enable),
+ __ATTR(hotplug, 0644, hdmi_show_hotplug, NULL),
+ __ATTR_NULL,
+};
+
+/**
+ * hdmi_dev_register - register a new object of hdmi_dev class.
+ * @name: the name of the new object(must be the same as the name of the
+ * respective framebuffer device).
+ * @devdata: an optional pointer to be stored in the device. The
+ * methods may retrieve it by using hdmi_get_data(hd).
+ * @ops: the hdmi operations structure.
+ *
+ * Creates and registers a new hdmi device. Returns either an ERR_PTR()
+ * or a pointer to the newly allocated device.
+ */
+int hdmi_dev_register(struct hdmi_dev *hd, struct device *parent,
+ void *devdata, struct hdmi_ops *ops, int fb_index)
+{
+ struct hdmi_event *evt_list;
+ int i;
+ int rc;
+
+ hd->dev = device_create(hdmi_class, parent,
+ MKDEV(HDMI_MAJOR, hdmidevno), hd, "hdmi%d", hdmidevno);
+ if (IS_ERR(hd->dev)) {
+ /* Not fatal */
+ hdmi_dprintk(1, "Unable to create device for hdmi %d; errno = %ld\n",
+ hdmidevno, PTR_ERR(hd->dev));
+ hd->dev = NULL;
+ }
+
+ spin_lock_init(&hd->lock);
+ mutex_init(&hd->hdmi_lock);
+ mutex_init(&hd->update_lock);
+
+ INIT_LIST_HEAD(&hd->hdmi_evt_list);
+ INIT_LIST_HEAD(&hd->free_evt_list);
+ hd->evt_ptr = evt_list =
+ kzalloc(MAX_HDMI_EVENT_SIZE * sizeof(struct hdmi_event), GFP_KERNEL);
+ for (i = 0; i < MAX_HDMI_EVENT_SIZE; i++) {
+ list_add(&evt_list->list, &hd->free_evt_list);
+ evt_list ++;
+ }
+
+ rc = hdmi_register_lcd(hd);
+ if (rc) {
+ device_unregister(hd->dev);
+ hdmi_dprintk(1, "Unable to create lcd device for hdmi\n");
+ goto dev_out;
+ }
+
+ hd->ops = ops;
+ hd->fb_info = registered_fb[fb_index];
+ if (! hd->fb_info) {
+ hdmi_dprintk(1, "Invalid associated fb\n");
+ goto dev_out;
+ }
+
+ /* Register CEC input device here */
+ /* Register audio Asoc device here */
+ /* Init hot plug uevent here */
+ /* Init HDCP here */
+
+ hd->thread = kthread_run(hdmi_event_thread, hd,
+ "hdmi%d", hdmidevno++);
+
+ if (IS_ERR(hd->thread)) {
+ rc = PTR_ERR(hd->thread);
+ hdmi_dprintk(1, "Unable to create internal thread for hdmi\n");
+ goto lcd_out;
+ }
+
+ return 0;
+
+ kthread_stop(hd->thread);
+lcd_out:
+ lcd_device_unregister(hd->lcd);
+dev_out:
+ device_unregister(hd->dev);
+ hdmi_dprintk(1, "Fail to register hdmi dev\n");
+ return rc;
+}
+EXPORT_SYMBOL(hdmi_dev_register);
+
+/**
+ * hdmi_dev_unregister - unregisters a object of hdmi_dev class.
+ * @hd: the hdmi device object to be unregistered and freed.
+ *
+ * Unregisters a previously registered via hdmi_dev_register object.
+ */
+void hdmi_dev_unregister(struct hdmi_dev *hd)
+{
+ if (!hd)
+ return;
+
+ mutex_lock(&hd->hdmi_lock);
+ hd->ops = NULL;
+ mutex_unlock(&hd->hdmi_lock);
+ lcd_device_unregister(hd->lcd);
+
+ kthread_stop(hd->thread);
+ device_unregister(hd->dev);
+}
+EXPORT_SYMBOL(hdmi_dev_unregister);
+
+static void __exit hdmi_class_exit(void)
+{
+ class_destroy(hdmi_class);
+}
+
+static int __init hdmi_class_init(void)
+{
+ hdmi_dprintk(1, "create hdmi class...\n");
+ hdmi_class = class_create(THIS_MODULE, "hdmi");
+ if (IS_ERR(hdmi_class)) {
+ hdmi_dprintk(1, "Unable to create hdmi class; errno = %ld\n",
+ PTR_ERR(hdmi_class));
+ return PTR_ERR(hdmi_class);
+ }
+
+ hdmi_class->dev_attrs = hdmi_dev_attributes;
+ hdmi_dprintk(1, "create hdmi class ok\n");
+ return 0;
+}
+
+subsys_initcall(hdmi_class_init);
+module_exit(hdmi_class_exit);
+
+module_param_named(debug, hdmi_core_debug, int, 0644);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index cb175fe..93f5b6e 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -292,62 +292,63 @@ static const struct fb_videomode modedb[] = {
};
#ifdef CONFIG_FB_MODE_HELPERS
-const struct fb_videomode cea_modes[64] = {
+const struct fb_videomode cea_modes[65] = {
/* #1: 640x480p@59.94/60Hz */
[1] = {
NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #3: 720x480p@59.94/60Hz */
[3] = {
NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #5: 1920x1080i@59.94/60Hz */
[5] = {
NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
},
/* #7: 720(1440)x480iH@59.94/60Hz */
[7] = {
NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
},
/* #9: 720(1440)x240pH@59.94/60Hz */
[9] = {
NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #18: 720x576pH@50Hz */
[18] = {
NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #19: 1280x720p@50Hz */
[19] = {
NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #20: 1920x1080i@50Hz */
[20] = {
NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_INTERLACED, 0,
+ FB_VMODE_INTERLACED, FB_MODE_IS_CEA,
},
/* #32: 1920x1080p@23.98/24Hz */
[32] = {
NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5,
FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
/* #35: (2880)x480p4x@59.94/60Hz */
[35] = {
NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0,
- FB_VMODE_NONINTERLACED, 0,
+ FB_VMODE_NONINTERLACED, FB_MODE_IS_CEA,
},
};
+EXPORT_SYMBOL(cea_modes);
const struct fb_videomode vesa_modes[] = {
/* 0 640x350-85 VESA */
@@ -1119,6 +1120,24 @@ finished:
}
EXPORT_SYMBOL(fb_find_best_display);
+/**
+ * fb_find_cea_mode_index - find a cea video mode index
+ *
+ * RETURNS:
+ * cea mode index, 0 if none found
+ */
+const int fb_find_cea_mode_index(struct fb_videomode *m)
+{
+ int idx;
+
+ for (idx = 1; idx <= 64; idx ++) {
+ if (fb_mode_is_equal(&cea_modes[idx], m))
+ return idx;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(fb_find_cea_mode_index);
+
EXPORT_SYMBOL(fb_videomode_to_var);
EXPORT_SYMBOL(fb_var_to_videomode);
EXPORT_SYMBOL(fb_mode_is_equal);
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 6a82748..14672f8 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1082,10 +1082,24 @@ extern void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max)
#define FB_MODE_IS_DETAILED 1
#define FB_MODE_IS_STANDARD 2
#define FB_MODE_IS_VESA 4
+#define FB_MODE_IS_CEA 5
#define FB_MODE_IS_CALCULATED 8
#define FB_MODE_IS_FIRST 16
#define FB_MODE_IS_FROM_VAR 32
+typedef enum {
+ CEA_640x480p60 = 1,
+ CEA_720x480p60 = 3,
+ CEA_1920x1080i60 = 5,
+ CEA_720x480i60 = 7,
+ CEA_720x240p60 = 9,
+ CEA_720x576p50 = 18,
+ CEA_1280x720p50 = 19,
+ CEA_1920x1080i50 = 20,
+ CEA_1920x1080p24 = 32,
+ CEA_2880x480p60 =35,
+} cea_mode_id;
+
extern int fbmon_dpms(const struct fb_info *fb_info);
extern int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var,
struct fb_info *info);
@@ -1124,6 +1138,7 @@ extern void fb_videomode_to_modelist(const struct fb_videomode *modedb, int num,
struct list_head *head);
extern const struct fb_videomode *fb_find_best_display(const struct fb_monspecs *specs,
struct list_head *head);
+extern const int fb_find_cea_mode_index(struct fb_videomode *m);
/* drivers/video/fbcmap.c */
extern int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp);
@@ -1155,7 +1170,7 @@ struct fb_videomode {
extern const char *fb_mode_option;
extern const struct fb_videomode vesa_modes[];
-extern const struct fb_videomode cea_modes[64];
+extern const struct fb_videomode cea_modes[65];
struct fb_modelist {
struct list_head list;
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
new file mode 100644
index 0000000..d8a3da9
--- /dev/null
+++ b/include/linux/hdmi.h
@@ -0,0 +1,164 @@
+/*
+ * HDMI Control Abstraction
+ *
+ */
+
+#ifndef _LINUX_HDMI_H
+#define _LINUX_HDMI_H
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+//#include <linux/notifier.h>
+#include <linux/fb.h>
+#include <linux/lcd.h>
+
+/* Notes on locking:
+ *
+ * hdmi_dev->ops_lock is an internal backlight lock protecting the ops
+ * field and no code outside the core should need to touch it.
+ *
+ * Access to set_power() is serialised by the update_lock mutex since
+ * most drivers seem to need this and historically get it wrong.
+ *
+ * Most drivers don't need locking on their get_power() method.
+ * If yours does, you need to implement it in the driver. You can use the
+ * update_lock mutex if appropriate.
+ *
+ * Any other use of the locks below is probably wrong.
+ */
+
+struct hdmi_dev;
+struct fb_info;
+
+struct hdmi_properties {
+ /* source or sink */
+ u16 type;
+};
+
+struct hdmi_ops {
+ int (*init)(struct hdmi_dev *);
+ int (*get_hpd)(struct hdmi_dev *);
+ int (*enable)(struct hdmi_dev *, int);
+ /* Get the LCD panel power status (0: full on, 1..3: controller
+ power on, flat panel power off, 4: full off), see FB_BLANK_XXX */
+ int (*get_power)(struct hdmi_dev *);
+ /* Enable or disable power to the LCD (0: on; 4: off, see FB_BLANK_XXX) */
+ int (*set_power)(struct hdmi_dev *, int power);
+ /* For debug */
+ int (*get_reg)(struct hdmi_dev *);
+ int (*set_reg)(struct hdmi_dev *, int offset, int v);
+ /* Get the current audio_cfg setting (0-max_audio_cfg) */
+ int (*get_audio_cfg)(struct hdmi_dev *);
+ /* Set LCD panel audio_cfg */
+ int (*set_audio_cfg)(struct hdmi_dev *, int audio_cfg);
+ int (*get_mode)(struct hdmi_dev *, struct fb_videomode *);
+ /* Set LCD panel mode (resolutions ...) */
+ int (*set_mode)(struct hdmi_dev *, cea_mode_id);
+ int (*get_enable)(struct hdmi_dev *);
+ int (*set_enable)(struct hdmi_dev *, int enable);
+ /* Check if given framebuffer device is the one LCD is bound to;
+ return 0 if not, !=0 if it is. If NULL, hdmi always matches the fb. */
+ int (*check_mode)(struct hdmi_dev *, struct fb_videomode *);
+};
+
+enum hdmi_event_type {
+ /* trigger gpio irq */
+ HDMI_EV_PLUG,
+ HDMI_EV_UNPLUG,
+ /* trigger in gpio irq or from fb change */
+ HDMI_EV_VIDEO,
+ /* trigger in gpio irq or from Asoc change */
+ HDMI_EV_AUDIO,
+ /* trigger from CEC/TV */
+ HDMI_EV_CEC,
+};
+
+struct hdmi_event {
+ enum hdmi_event_type type;
+ struct list_head list;
+};
+
+#define RATIO_16V9_STR "16:9"
+#define RATIO_4V3_STR "4:3"
+#define RATIO_16V9 (0)
+#define RATIO_4V3 (1)
+
+struct hdmi_dev {
+ struct device *dev;
+ struct hdmi_properties props;
+ /* This protects the 'ops' field. If 'ops' is NULL, the driver that
+ registered this device has been unloaded, and if class_get_devdata()
+ points to something in the body of that driver, it is also invalid. */
+ struct mutex hdmi_lock;
+ /* Serialise access to set_power method */
+ struct mutex update_lock;
+ spinlock_t lock; /* lock for claim and bus ops */
+ struct list_head hdmi_evt_list;
+ struct list_head free_evt_list;
+ struct task_struct *thread;
+ struct hdmi_event *evt_ptr;
+ /* If this is NULL, the backing module is unloaded */
+ struct hdmi_ops *ops;
+ /* The framebuffer notifier block */
+ //struct notifier_block fb_notif;
+ struct lcd_device *lcd;
+ struct fb_info *fb_info;
+ cea_mode_id mode_id;
+ //u32 deep_color;
+ u32 enable;
+ u32 pixel_rept;
+ u32 ratio;
+};
+
+struct hdmi_platform_data {
+ /* reset hdmi panel device. */
+ int (*reset)(struct hdmi_dev *ld);
+ /* on or off to hdmi panel. if 'enable' is 0 then
+ hdmi power off and 1, hdmi power on. */
+ int (*power_on)(struct hdmi_dev *ld, int enable);
+
+ /* it indicates whether hdmi panel was enabled
+ from bootloader or not. */
+ int hdmi_enabled;
+ /* video, audio, cec, hdcp */
+ unsigned int features;
+ /* it means delay for stable time when it becomes low to high
+ or high to low that is dependent on whether reset gpio is
+ low active or high active. */
+ unsigned int reset_delay;
+ /* stable time needing to become hdmi power on. */
+ unsigned int power_on_delay;
+ /* stable time needing to become hdmi power off. */
+ unsigned int power_off_delay;
+
+ /* it could be used for any purpose. */
+ void *pdata;
+};
+
+static inline void hdmi_set_power(struct hdmi_dev *ld, int power)
+{
+ mutex_lock(&ld->update_lock);
+ if (ld->ops && ld->ops->set_power)
+ ld->ops->set_power(ld, power);
+ mutex_unlock(&ld->update_lock);
+}
+
+extern int hdmi_dev_register(struct hdmi_dev *hdmi_dev,
+ struct device *parent, void *devdata, struct hdmi_ops *ops,
+ int fb_index);
+extern void hdmi_dev_unregister(struct hdmi_dev *ld);
+
+#define to_hdmi_dev(obj) container_of(obj, struct hdmi_dev, dev)
+
+static inline void * hdmi_get_data(struct hdmi_dev *ld_dev)
+{
+ return dev_get_drvdata(ld_dev->dev);
+}
+
+
+#endif
+
+int hdmi_get_freq (struct hdmi_dev *hd, u32 *need_freq);
+int hdmi_vender_info_frame (struct hdmi_dev *hd, char buf[]);
+int hdmi_avi_info_frame (struct hdmi_dev *hd, char buf[]);
+void hdmi_event_signal(struct hdmi_dev *dev, enum hdmi_event_type type);
diff --git a/include/linux/lcd.h b/include/linux/lcd.h
index 8877123..b09e0f9 100644
--- a/include/linux/lcd.h
+++ b/include/linux/lcd.h
@@ -48,6 +48,8 @@ struct lcd_ops {
int (*set_contrast)(struct lcd_device *, int contrast);
/* Set LCD panel mode (resolutions ...) */
int (*set_mode)(struct lcd_device *, struct fb_videomode *);
+ /* Get LCD panel mode (resolutions ...) */
+ int (*get_mode)(struct lcd_device *, struct fb_videomode *);
/* Check if given framebuffer device is the one LCD is bound to;
return 0 if not, !=0 if it is. If NULL, lcd always matches the fb. */
int (*check_fb)(struct lcd_device *, struct fb_info *);
diff --git a/include/linux/major.h b/include/linux/major.h
index 6a8ca98..e6e46e2 100644
--- a/include/linux/major.h
+++ b/include/linux/major.h
@@ -170,6 +170,7 @@
#define IBM_FS3270_MAJOR 228
#define VIOTAPE_MAJOR 230
+#define HDMI_MAJOR 231
#define BLOCK_EXT_MAJOR 259
#define SCSI_OSD_MAJOR 260 /* open-osd's OSD scsi device */
^ permalink raw reply related
* [PATCH] video: s3c-fb: fix transparency length for pixel blending
From: Jingoo Han @ 2011-11-07 6:03 UTC (permalink / raw)
To: linux-fbdev
Transparency length is fixed for pixel blending in order to support
ARGB 8888 format.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
drivers/video/s3c-fb.c | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 0753b1c..12eaee0 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -621,7 +621,8 @@ static int s3c_fb_set_par(struct fb_info *info)
} else if (var->transp.length = 1)
data |= WINCON1_BPPMODE_25BPP_A1888
| WINCON1_BLD_PIX;
- else if (var->transp.length = 4)
+ else if ((var->transp.length = 4) ||
+ (var->transp.length = 8))
data |= WINCON1_BPPMODE_28BPP_A4888
| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
else
--
1.7.1
^ permalink raw reply related
* Re: [PATCH v3] GIO bus support for SGI IP22/28
From: Ralf Baechle @ 2011-11-04 11:22 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-fbdev, FlorianSchandinat
In-Reply-To: <20111104110905.531871BB71D@solo.franken.de>
Thanks,
queued for 3.3.
Ralf
^ permalink raw reply
* [PATCH v3] GIO bus support for SGI IP22/28
From: Thomas Bogendoerfer @ 2011-11-04 11:09 UTC (permalink / raw)
To: linux-mips, linux-fbdev; +Cc: ralf, FlorianSchandinat
SGI IP22/IP28 machines have GIO busses for adding graphics and other
extension cards. This patch adds support for GIO driver/device
handling and converts the newport console driver to a GIO driver.
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Acked-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
---
Changes in v3:
- reworked device tree for gio bus
- make module autoloading working
Changes in v2:
- use EXPORT_SYMBOL_GPL
- ChallengeS has only 2 slots
- use subsys_initcall for gio detection
arch/mips/include/asm/gio_device.h | 56 +++++
arch/mips/sgi-ip22/Makefile | 2 +-
arch/mips/sgi-ip22/ip22-gio.c | 428 +++++++++++++++++++++++++++++++++++
arch/mips/sgi-ip22/ip22-mc.c | 10 +-
arch/mips/sgi-ip22/ip22-setup.c | 21 --
drivers/video/console/newport_con.c | 63 ++++--
6 files changed, 534 insertions(+), 46 deletions(-)
diff --git a/arch/mips/include/asm/gio_device.h b/arch/mips/include/asm/gio_device.h
new file mode 100644
index 0000000..5437c84
--- /dev/null
+++ b/arch/mips/include/asm/gio_device.h
@@ -0,0 +1,56 @@
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+struct gio_device_id {
+ __u8 id;
+};
+
+struct gio_device {
+ struct device dev;
+ struct resource resource;
+ unsigned int irq;
+ unsigned int slotno;
+
+ const char *name;
+ struct gio_device_id id;
+ unsigned id32:1;
+ unsigned gio64:1;
+};
+#define to_gio_device(d) container_of(d, struct gio_device, dev)
+
+struct gio_driver {
+ const char *name;
+ struct module *owner;
+ const struct gio_device_id *id_table;
+
+ int (*probe)(struct gio_device *, const struct gio_device_id *);
+ void (*remove)(struct gio_device *);
+ int (*suspend)(struct gio_device *, pm_message_t);
+ int (*resume)(struct gio_device *);
+ void (*shutdown)(struct gio_device *);
+
+ struct device_driver driver;
+};
+#define to_gio_driver(drv) container_of(drv, struct gio_driver, driver)
+
+extern const struct gio_device_id *gio_match_device(const struct gio_device_id *,
+ const struct gio_device *);
+extern struct gio_device *gio_dev_get(struct gio_device *);
+extern void gio_dev_put(struct gio_device *);
+
+extern int gio_device_register(struct gio_device *);
+extern void gio_device_unregister(struct gio_device *);
+extern void gio_release_dev(struct device *);
+
+static inline void gio_device_free(struct gio_device *dev)
+{
+ gio_release_dev(&dev->dev);
+}
+
+extern int gio_register_driver(struct gio_driver *);
+extern void gio_unregister_driver(struct gio_driver *);
+
+#define gio_get_drvdata(_dev) drv_get_drvdata(&(_dev)->dev)
+#define gio_set_drvdata(_dev, data) drv_set_drvdata(&(_dev)->dev, (data))
+
+extern void gio_set_master(struct gio_device *);
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile
index cc53849..411cda9 100644
--- a/arch/mips/sgi-ip22/Makefile
+++ b/arch/mips/sgi-ip22/Makefile
@@ -4,7 +4,7 @@
#
obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-time.o ip22-nvram.o \
- ip22-platform.o ip22-reset.o ip22-setup.o
+ ip22-platform.o ip22-reset.o ip22-setup.o ip22-gio.o
obj-$(CONFIG_SGI_IP22) += ip22-berr.o
obj-$(CONFIG_SGI_IP28) += ip28-berr.o
diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c
new file mode 100644
index 0000000..b6537d9
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-gio.c
@@ -0,0 +1,428 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include <asm/addrspace.h>
+#include <asm/paccess.h>
+#include <asm/gio_device.h>
+#include <asm/sgi/gio.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/ip22.h>
+
+static struct bus_type gio_bus_type;
+
+static struct {
+ const char *name;
+ __u8 id;
+} gio_name_table[] = {
+ { .name = "SGI Impact", .id = 0x10 },
+ { .name = "Phobos G160", .id = 0x35 },
+ /* fake IDs */
+ { .name = "SGI Newport", .id = 0x7e },
+ { .name = "SGI GR2/GR3", .id = 0x7f },
+};
+
+static struct device gio_bus = {
+ .init_name = "gio",
+};
+
+/**
+ * gio_match_device - Tell if an of_device structure has a matching
+ * gio_match structure
+ * @ids: array of of device match structures to search in
+ * @dev: the of device structure to match against
+ *
+ * Used by a driver to check whether an of_device present in the
+ * system is in its list of supported devices.
+ */
+const struct gio_device_id *gio_match_device(const struct gio_device_id *match,
+ const struct gio_device *dev)
+{
+ const struct gio_device_id *ids;
+
+ for (ids = match; ids->id != 0xff; ids++)
+ if (ids->id = dev->id.id)
+ return ids;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gio_match_device);
+
+struct gio_device *gio_dev_get(struct gio_device *dev)
+{
+ struct device *tmp;
+
+ if (!dev)
+ return NULL;
+ tmp = get_device(&dev->dev);
+ if (tmp)
+ return to_gio_device(tmp);
+ else
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gio_dev_get);
+
+void gio_dev_put(struct gio_device *dev)
+{
+ if (dev)
+ put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_dev_put);
+
+/**
+ * gio_release_dev - free an gio device structure when all users of it are finished.
+ * @dev: device that's been disconnected
+ *
+ * Will be called only by the device core when all users of this gio device are
+ * done.
+ */
+void gio_release_dev(struct device *dev)
+{
+ struct gio_device *giodev;
+
+ giodev = to_gio_device(dev);
+ kfree(giodev);
+}
+EXPORT_SYMBOL_GPL(gio_release_dev);
+
+int gio_device_register(struct gio_device *giodev)
+{
+ giodev->dev.bus = &gio_bus_type;
+ giodev->dev.parent = &gio_bus;
+ return device_register(&giodev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_device_register);
+
+void gio_device_unregister(struct gio_device *giodev)
+{
+ device_unregister(&giodev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_device_unregister);
+
+static int gio_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ struct gio_driver *gio_drv = to_gio_driver(drv);
+
+ return gio_match_device(gio_drv->id_table, gio_dev) != NULL;
+}
+
+static int gio_device_probe(struct device *dev)
+{
+ int error = -ENODEV;
+ struct gio_driver *drv;
+ struct gio_device *gio_dev;
+ const struct gio_device_id *match;
+
+ drv = to_gio_driver(dev->driver);
+ gio_dev = to_gio_device(dev);
+
+ if (!drv->probe)
+ return error;
+
+ gio_dev_get(gio_dev);
+
+ match = gio_match_device(drv->id_table, gio_dev);
+ if (match)
+ error = drv->probe(gio_dev, match);
+ if (error)
+ gio_dev_put(gio_dev);
+
+ return error;
+}
+
+static int gio_device_remove(struct device *dev)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ struct gio_driver *drv = to_gio_driver(dev->driver);
+
+ if (dev->driver && drv->remove)
+ drv->remove(gio_dev);
+ return 0;
+}
+
+static int gio_device_suspend(struct device *dev, pm_message_t state)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ struct gio_driver *drv = to_gio_driver(dev->driver);
+ int error = 0;
+
+ if (dev->driver && drv->suspend)
+ error = drv->suspend(gio_dev, state);
+ return error;
+}
+
+static int gio_device_resume(struct device *dev)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ struct gio_driver *drv = to_gio_driver(dev->driver);
+ int error = 0;
+
+ if (dev->driver && drv->resume)
+ error = drv->resume(gio_dev);
+ return error;
+}
+
+static void gio_device_shutdown(struct device *dev)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ struct gio_driver *drv = to_gio_driver(dev->driver);
+
+ if (dev->driver && drv->shutdown)
+ drv->shutdown(gio_dev);
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ int len = snprintf(buf, PAGE_SIZE, "gio:%x\n", gio_dev->id.id);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gio_device *giodev;
+
+ giodev = to_gio_device(dev);
+ return sprintf(buf, "%s", giodev->name);
+}
+
+static ssize_t id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gio_device *giodev;
+
+ giodev = to_gio_device(dev);
+ return sprintf(buf, "%x", giodev->id.id);
+}
+
+static struct device_attribute gio_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_RO(name),
+ __ATTR_RO(id),
+ __ATTR_NULL,
+};
+
+static int gio_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+
+ add_uevent_var(env, "MODALIAS=gio:%x", gio_dev->id.id);
+ return 0;
+}
+
+int gio_register_driver(struct gio_driver *drv)
+{
+ /* initialize common driver fields */
+ if (!drv->driver.name)
+ drv->driver.name = drv->name;
+ if (!drv->driver.owner)
+ drv->driver.owner = drv->owner;
+ drv->driver.bus = &gio_bus_type;
+
+ /* register with core */
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(gio_register_driver);
+
+void gio_unregister_driver(struct gio_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(gio_unregister_driver);
+
+void gio_set_master(struct gio_device *dev)
+{
+ u32 tmp = sgimc->giopar;
+
+ switch (dev->slotno) {
+ case 0:
+ tmp |= SGIMC_GIOPAR_MASTERGFX;
+ break;
+ case 1:
+ tmp |= SGIMC_GIOPAR_MASTEREXP0;
+ break;
+ case 2:
+ tmp |= SGIMC_GIOPAR_MASTEREXP1;
+ break;
+ }
+ sgimc->giopar = tmp;
+}
+EXPORT_SYMBOL_GPL(gio_set_master);
+
+void ip22_gio_set_64bit(int slotno)
+{
+ u32 tmp = sgimc->giopar;
+
+ switch (slotno) {
+ case 0:
+ tmp |= SGIMC_GIOPAR_GFX64;
+ break;
+ case 1:
+ tmp |= SGIMC_GIOPAR_EXP064;
+ break;
+ case 2:
+ tmp |= SGIMC_GIOPAR_EXP164;
+ break;
+ }
+ sgimc->giopar = tmp;
+}
+
+static int ip22_gio_id(unsigned long addr, u32 *res)
+{
+ u8 tmp8;
+ u8 tmp16;
+ u32 tmp32;
+ u8 *ptr8;
+ u16 *ptr16;
+ u32 *ptr32;
+
+ ptr32 = (void *)CKSEG1ADDR(addr);
+ if (!get_dbe(tmp32, ptr32)) {
+ /*
+ * We got no DBE, but this doesn't mean anything.
+ * If GIO is pipelined (which can't be disabled
+ * for GFX slot) we don't get a DBE, but we see
+ * the transfer size as data. So we do an 8bit
+ * and a 16bit access and check whether the common
+ * data matches
+ */
+ ptr8 = (void *)CKSEG1ADDR(addr + 3);
+ get_dbe(tmp8, ptr8);
+ ptr16 = (void *)CKSEG1ADDR(addr + 2);
+ get_dbe(tmp16, ptr16);
+ if (tmp8 = (tmp16 & 0xff) &&
+ tmp8 = (tmp32 & 0xff) &&
+ tmp16 = (tmp32 & 0xffff)) {
+ *res = tmp32;
+ return 1;
+ }
+ }
+ return 0; /* nothing here */
+}
+
+#define HQ2_MYSTERY_OFFS 0x6A07C
+#define NEWPORT_USTATUS_OFFS 0xF133C
+
+static int ip22_is_gr2(unsigned long addr)
+{
+ u32 tmp;
+ u32 *ptr;
+
+ /* HQ2 only allows 32bit accesses */
+ ptr = (void *)CKSEG1ADDR(addr + HQ2_MYSTERY_OFFS);
+ if (!get_dbe(tmp, ptr)) {
+ if (tmp = 0xdeadbeef)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void ip22_check_gio(int slotno, unsigned long addr)
+{
+ const char *name = "Unknown";
+ struct gio_device *gio_dev;
+ u32 tmp;
+ __u8 id;
+ int i;
+
+ /* first look for GR2/GR3 by checking mystery register */
+ if (ip22_is_gr2(addr))
+ tmp = 0x7f;
+ else {
+ if (!ip22_gio_id(addr, &tmp)) {
+ /*
+ * no GIO signature at start address of slot, but
+ * Newport doesn't have one, so let's check usea
+ * status register
+ */
+ if (ip22_gio_id(addr + NEWPORT_USTATUS_OFFS, &tmp))
+ tmp = 0x7e;
+ else
+ tmp = 0;
+ }
+ }
+ if (tmp) {
+ id = GIO_ID(tmp);
+ if (tmp & GIO_32BIT_ID) {
+ if (tmp & GIO_64BIT_IFACE)
+ ip22_gio_set_64bit(slotno);
+ }
+ for (i = 0; i < ARRAY_SIZE(gio_name_table); i++) {
+ if (id = gio_name_table[i].id) {
+ name = gio_name_table[i].name;
+ break;
+ }
+ }
+ printk(KERN_INFO "GIO: slot %d : %s (id %x)\n",
+ slotno, name, id);
+ gio_dev = kzalloc(sizeof *gio_dev, GFP_KERNEL);
+ gio_dev->name = name;
+ gio_dev->slotno = slotno;
+ gio_dev->id.id = id;
+ gio_dev->resource.start = addr;
+ gio_dev->resource.end = addr + 0x3fffff;
+ gio_dev->resource.flags = IORESOURCE_MEM;
+ dev_set_name(&gio_dev->dev, "%d", slotno);
+ gio_device_register(gio_dev);
+ } else
+ printk(KERN_INFO "GIO: slot %d : Empty\n", slotno);
+}
+
+static struct bus_type gio_bus_type = {
+ .name = "gio",
+ .dev_attrs = gio_dev_attrs,
+ .match = gio_bus_match,
+ .probe = gio_device_probe,
+ .remove = gio_device_remove,
+ .suspend = gio_device_suspend,
+ .resume = gio_device_resume,
+ .shutdown = gio_device_shutdown,
+ .uevent = gio_device_uevent,
+};
+
+static struct resource gio_bus_resource = {
+ .start = GIO_SLOT_GFX_BASE,
+ .end = GIO_SLOT_GFX_BASE + 0x9fffff,
+ .name = "GIO Bus",
+ .flags = IORESOURCE_MEM,
+};
+
+int __init ip22_gio_init(void)
+{
+ unsigned int pbdma __maybe_unused;
+ int ret;
+
+ ret = device_register(&gio_bus);
+ if (ret)
+ return ret;
+
+ ret = bus_register(&gio_bus_type);
+ if (!ret) {
+ request_resource(&iomem_resource, &gio_bus_resource);
+ printk(KERN_INFO "GIO: Probing bus...\n");
+
+ if (ip22_is_fullhouse() ||
+ !get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1])) {
+ /* Indigo2 and ChallengeS */
+ ip22_check_gio(0, GIO_SLOT_GFX_BASE);
+ ip22_check_gio(1, GIO_SLOT_EXP0_BASE);
+ } else {
+ /* Indy */
+ ip22_check_gio(0, GIO_SLOT_GFX_BASE);
+ ip22_check_gio(1, GIO_SLOT_EXP0_BASE);
+ ip22_check_gio(2, GIO_SLOT_EXP1_BASE);
+ }
+ } else
+ device_unregister(&gio_bus);
+
+ return ret;
+}
+
+subsys_initcall(ip22_gio_init);
+
diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c
index d22262e..75ada8a 100644
--- a/arch/mips/sgi-ip22/ip22-mc.c
+++ b/arch/mips/sgi-ip22/ip22-mc.c
@@ -139,11 +139,11 @@ void __init sgimc_init(void)
* zero.
*/
/* don't touch parity settings for IP28 */
-#ifndef CONFIG_SGI_IP28
tmp = sgimc->cpuctrl0;
- tmp |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM |
- SGIMC_CCTRL0_R4KNOCHKPARR);
+#ifndef CONFIG_SGI_IP28
+ tmp |= SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM;
#endif
+ tmp |= SGIMC_CCTRL0_R4KNOCHKPARR;
sgimc->cpuctrl0 = tmp;
/* Step 3: Setup the MC write buffer depth, this is controlled
@@ -178,7 +178,8 @@ void __init sgimc_init(void)
*/
/* First the basic invariants across all GIO64 implementations. */
- tmp = SGIMC_GIOPAR_HPC64; /* All 1st HPC's interface at 64bits */
+ tmp = sgimc->giopar & SGIMC_GIOPAR_GFX64; /* keep gfx 64bit settings */
+ tmp |= SGIMC_GIOPAR_HPC64; /* All 1st HPC's interface at 64bits */
tmp |= SGIMC_GIOPAR_ONEBUS; /* Only one physical GIO bus exists */
if (ip22_is_fullhouse()) {
@@ -193,7 +194,6 @@ void __init sgimc_init(void)
tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp[01] pipelined */
tmp |= SGIMC_GIOPAR_PLINEEXP1;
tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA masters */
- tmp |= SGIMC_GIOPAR_GFX64; /* GFX at 64 bits */
}
} else {
/* Guiness specific settings. */
diff --git a/arch/mips/sgi-ip22/ip22-setup.c b/arch/mips/sgi-ip22/ip22-setup.c
index 5e66213..c7bdfe4 100644
--- a/arch/mips/sgi-ip22/ip22-setup.c
+++ b/arch/mips/sgi-ip22/ip22-setup.c
@@ -26,9 +26,6 @@
#include <asm/sgi/hpc3.h>
#include <asm/sgi/ip22.h>
-unsigned long sgi_gfxaddr;
-EXPORT_SYMBOL_GPL(sgi_gfxaddr);
-
extern void ip22_be_init(void) __init;
void __init plat_mem_setup(void)
@@ -78,22 +75,4 @@ void __init plat_mem_setup(void)
prom_flags |= PROM_FLAG_USE_AS_CONSOLE;
add_preferred_console("arc", 0, NULL);
}
-
-#if defined(CONFIG_VT) && defined(CONFIG_SGI_NEWPORT_CONSOLE)
- {
- ULONG *gfxinfo;
- ULONG * (*__vec)(void) = (void *) (long)
- *((_PULONG *)(long)((PROMBLOCK)->pvector + 0x20));
-
- gfxinfo = __vec();
- sgi_gfxaddr = ((gfxinfo[1] >= 0xa0000000
- && gfxinfo[1] <= 0xc0000000)
- ? gfxinfo[1] - 0xa0000000 : 0);
-
- /* newport addresses? */
- if (sgi_gfxaddr = 0x1f0f0000 || sgi_gfxaddr = 0x1f4f0000) {
- conswitchp = &newport_con;
- }
- }
-#endif
}
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index 93317b5..a122d92 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -25,14 +25,13 @@
#include <asm/system.h>
#include <asm/page.h>
#include <asm/pgtable.h>
+#include <asm/gio_device.h>
+
#include <video/newport.h>
#include <linux/linux_logo.h>
#include <linux/font.h>
-
-extern unsigned long sgi_gfxaddr;
-
#define FONT_DATA ((unsigned char *)font_vga_8x16.data)
/* borrowed from fbcon.c */
@@ -304,12 +303,6 @@ static const char *newport_startup(void)
{
int i;
- if (!sgi_gfxaddr)
- return NULL;
-
- if (!npregs)
- npregs = (struct newport_regs *)/* ioremap cannot fail */
- ioremap(sgi_gfxaddr, sizeof(struct newport_regs));
npregs->cset.config = NPORT_CFG_GD0;
if (newport_wait(npregs))
@@ -743,26 +736,58 @@ const struct consw newport_con = {
.con_save_screen = DUMMY
};
-#ifdef MODULE
-static int __init newport_console_init(void)
+static int newport_probe(struct gio_device *dev,
+ const struct gio_device_id *id)
{
- if (!sgi_gfxaddr)
- return 0;
+ unsigned long newport_addr;
- if (!npregs)
- npregs = (struct newport_regs *)/* ioremap cannot fail */
- ioremap(sgi_gfxaddr, sizeof(struct newport_regs));
+ if (!dev->resource.start)
+ return -EINVAL;
+
+ if (npregs)
+ return -EBUSY; /* we only support one Newport as console */
+
+ newport_addr = dev->resource.start + 0xF0000;
+ if (!request_mem_region(newport_addr, 0x10000, "Newport"))
+ return -ENODEV;
+
+ npregs = (struct newport_regs *)/* ioremap cannot fail */
+ ioremap(newport_addr, sizeof(struct newport_regs));
return take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1);
}
-module_init(newport_console_init);
-static void __exit newport_console_exit(void)
+static void newport_remove(struct gio_device *dev)
{
give_up_console(&newport_con);
iounmap((void *)npregs);
}
+
+static struct gio_device_id newport_ids[] = {
+ { .id = 0x7e },
+ { .id = 0xff }
+};
+
+MODULE_ALIAS("gio:7e");
+
+static struct gio_driver newport_driver = {
+ .name = "newport",
+ .id_table = newport_ids,
+ .probe = newport_probe,
+ .remove = newport_remove,
+};
+
+int __init newport_console_init(void)
+{
+ return gio_register_driver(&newport_driver);
+}
+
+void __exit newport_console_exit(void)
+{
+ gio_unregister_driver(&newport_driver);
+}
+
+module_init(newport_console_init);
module_exit(newport_console_exit);
-#endif
MODULE_LICENSE("GPL");
^ permalink raw reply related
* Re: [RFC] Virtual CRTCs (proposal + experimental code)
From: Ilija Hadzic @ 2011-11-03 18:00 UTC (permalink / raw)
To: David Airlie; +Cc: linux-fbdev, dri-devel
In-Reply-To: <e80977e3-0992-454e-8ee2-2944560d3b3d@zmail16.collab.prod.int.phx2.redhat.com>
On Thu, 3 Nov 2011, David Airlie wrote:
>
> Well the current plan I had for this was to do it in userspace, I don't think the kernel
> has any business doing it and I think for the simple USB case its fine but will fallover
> when you get to the non-trivial cases where some sort of acceleration is required to move
> pixels around. But in saying that its good you've done what something, and I'll try and spend
> some time reviewing it.
>
The reason I opted for doing this in kernel is that I wanted to confine
all the changes to a relatively small set of modules. At first this was a
pragmatic approach, because I live out of the mainstream development tree
and I didn't want to turn my life into an ethernal
merging/conflict-resolution activity.
However, a more fundamental reason for it is that I didn't want to be tied
to X. I deal with some userland applications (that unfortunately I can't
provide much detail of .... yet) that live directly on the top of libdrm.
So I set myself a goal of "full application transparency". Whatever is
thrown at me, I wanted to be able to handle without having to touch any
piece of application or library that the application relies on.
I think I have achieved this goal and really everything I tried just
worked out of the box (with an exception of two bug fixes to ATI DDX
and Xorg, that are bugs with or without my work).
-- Ilija
^ permalink raw reply
* Re: [RFC] Virtual CRTCs (proposal + experimental code)
From: Alan Cox @ 2011-11-03 17:53 UTC (permalink / raw)
To: David Airlie; +Cc: linux-fbdev, dri-devel
In-Reply-To: <e80977e3-0992-454e-8ee2-2944560d3b3d@zmail16.collab.prod.int.phx2.redhat.com>
> Well the current plan I had for this was to do it in userspace, I don't think the kernel
> has any business doing it and I think for the simple USB case its fine but will fallover
> when you get to the non-trivial cases where some sort of acceleration is required to move
> pixels around. But in saying that its good you've done what something, and I'll try and spend
> some time reviewing it.
There are some clear advantages in the kernel doing bits of this I think.
The kernel understands device to device DMA, and has a better idea than
userspace about things like buffer alignment internals. It also means
this ultimately can work without X running which is a plus for some
applications (I want a displaylink gadget for my phone but thats another
story 8)).
> I'm also not sure how you deal with tiling etc, you can also start hitting rendering limits,
> where a GPU can render to 4kx4k but you can plug in more USB devices, again I'm hoping to
> solve this in userspace as well.
Tiling has to be handled by the recipient (at least when the fb is shared). The nastier end of
it that I don't see convered in the documentation is the handling of
fencing between cards. Eg if you wanted to do display on one card fed
into a second to do effects processing (think about TV type stuff)
Alan
^ permalink raw reply
* Re: [RFC] Virtual CRTCs (proposal + experimental code)
From: David Airlie @ 2011-11-03 17:27 UTC (permalink / raw)
To: Ilija Hadzic; +Cc: linux-fbdev, dri-devel
In-Reply-To: <CA+4h6Hk3gE+8ou6NSMhoGJWZJ-5aM1OgC0HFoKs55fjfQ-qN8g@mail.gmail.com>
>
> Hi everyone,
>
> I would like to bring to the attention of dri-devel and linux-fbdev
> community a set of hopefully useful and interesting patches that
> I (and a few other colleagues) have been working on during the past
> few months. Here, I will provide a short abstract, so that you can
> decide whether this is of interest for you. At the end, I will
> provide the pointers to the code and documentation.
>
> The code is based on Dave Arilie's tree, drm-next branch and it
> allows a GPU driver to have an arbitrary number of CRTCs
> (configurable by user) instead of only those CRTCs that represent
> real hardware.
Well the current plan I had for this was to do it in userspace, I don't think the kernel
has any business doing it and I think for the simple USB case its fine but will fallover
when you get to the non-trivial cases where some sort of acceleration is required to move
pixels around. But in saying that its good you've done what something, and I'll try and spend
some time reviewing it.
The current plan from my POV is to add hotplug support to the X server and just hotplug USB
devices up there, and not mess up the kernel with lots of extra state that really the drivers
don't need to know about.
I'm also not sure how you deal with tiling etc, you can also start hitting rendering limits,
where a GPU can render to 4kx4k but you can plug in more USB devices, again I'm hoping to
solve this in userspace as well.
But I'll take some time if I can find it to look over it.
Dave.
^ permalink raw reply
* Re: [RFC] Virtual CRTCs (proposal + experimental code)
From: Daniel Vetter @ 2011-11-03 17:21 UTC (permalink / raw)
To: Ilija Hadzic; +Cc: linux-fbdev, dri-devel
In-Reply-To: <CA+4h6Hk3gE+8ou6NSMhoGJWZJ-5aM1OgC0HFoKs55fjfQ-qN8g@mail.gmail.com>
Hi,
Quick question: How does this compare to Dave Airlies' PRIME buffer
sharing work for drm respectively the more generic dma_buf buffer sharing
work pushed by Linaro? You seem to aim for a solution for similar problems
(judging by your description) using a rather different approach.
Cheers, Daniel
On Thu, Nov 03, 2011 at 11:59:21AM -0400, Ilija Hadzic wrote:
> Hi everyone,
>
> I would like to bring to the attention of dri-devel and linux-fbdev
> community a set of hopefully useful and interesting patches that
> I (and a few other colleagues) have been working on during the past
> few months. Here, I will provide a short abstract, so that you can
> decide whether this is of interest for you. At the end, I will
> provide the pointers to the code and documentation.
>
> The code is based on Dave Arilie's tree, drm-next branch and it
> allows a GPU driver to have an arbitrary number of CRTCs
> (configurable by user) instead of only those CRTCs that represent
> real hardware.
>
> The new CRTCs, that we call Virtual CRTCs can be attached to a
> foreign device, that we call CTD device (short for Compression
> Transmission and Display), and pixels can be streamed out of the GPU
> to the device.
>
> In one example, we use AMD/ATI Radeon GPU to do 3D rendering
> (accelerated, of course) and we use our code to add additional
> monitor heads using DisplayLink devices. In other words, we achieve
> accelerated 3D rendering on a DisplayLink monitor. In another example
> we funnel rendered pixels to userland by emulating a Video-for-Linux
> device (and then userland can do whatever it wants with it). While
> doing all this, GPU has no idea that we are doing this, the entire DRI
> "thinks" that it is just dealing with a GPU that has a few "extra"
> connectors and CRTCs. So everything ports without the need to modify
> anything in the userland.
>
> In general any device that can do something good with rendered pixels
> can act as a CTD device, allowing a GPU to be an acceleration device
> for a less capable display device or (the opposite) a frame-buffer-based
> display device to be an expansion card for a GPU. Of course, for
> each display device, a driver would have to be made compatible with our
> new infrastructure (which is what we have done with DisplayLink driver
> and also wrote one "synthetic" driver to fake out a V4L2 device as a
> CTD device).
>
> The newly introduced kernel module that we call VCRTCM (short for
> Virtual CRTC Manager) handles the "traffic" between GPUs (actually
> their CRTCs) and CTDs. The code makes use of DMA wherever possible
> and also deals with specifics of CRTCs like modes, vblanks, page
> flips, hardware cursor, etc. (just for kick, we played OpenArena
> and watched Unigine Heaven demo on a DisplayLink monitor).
>
> At this time, we would like to solicit feedback, comments, and
> possibly contributions. The code is on github (pointers below)
> and is based on the current state of drm-next branch from Dave's
> tree. The code is very experimental, but complete and stable enough
> that you can do something useful with it. We will be adding more
> CTD drivers and updates to current ones in the near future and will
> continue to maintain the code on github.
>
> If the community finds this useful, we would be glad to work with
> the maintainers on merging this upstream. So we would especially like
> to hear what you would like to see changed to make this code acceptable
> for the mainline of development.
>
> My Github page is at https://github.com/ihadzic. To access the kernel
> code type:
>
> $ git clone git://github.com/ihadzic/linux-vcrtcm.git
> $ git branch drm-next-vcrtcm origin/drm-next-vcrtcm
> $ git checkout drm-next-vcrtcm
>
> You will get all that's currently on Dave's drm-next plus our patches on
> the top. We kept the development history preserved without squashing patches
> (unless we had to due to merge/rebase conflicts), so you can see (and laugh
> at) all our goofs and fixes to them.
>
> To access the documentation, type:
>
> $ git clone git://github.com/ihadzic/vcrtcm-doc.git
>
> Then read the HOWTO.txt file. The first few sections provide some
> general overview, and the sections that come later provide instructions
> how to use our stuff.
>
> Again, all comments, positive or negative, are very welcome.
>
> -- Ilija
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Mail: daniel@ffwll.ch
Mobile: +41 (0)79 365 57 48
^ permalink raw reply
* [RFC] Virtual CRTCs (proposal + experimental code)
From: Ilija Hadzic @ 2011-11-03 15:59 UTC (permalink / raw)
To: dri-devel, linux-fbdev
Hi everyone,
I would like to bring to the attention of dri-devel and linux-fbdev
community a set of hopefully useful and interesting patches that
I (and a few other colleagues) have been working on during the past
few months. Here, I will provide a short abstract, so that you can
decide whether this is of interest for you. At the end, I will
provide the pointers to the code and documentation.
The code is based on Dave Arilie's tree, drm-next branch and it
allows a GPU driver to have an arbitrary number of CRTCs
(configurable by user) instead of only those CRTCs that represent
real hardware.
The new CRTCs, that we call Virtual CRTCs can be attached to a
foreign device, that we call CTD device (short for Compression
Transmission and Display), and pixels can be streamed out of the GPU
to the device.
In one example, we use AMD/ATI Radeon GPU to do 3D rendering
(accelerated, of course) and we use our code to add additional
monitor heads using DisplayLink devices. In other words, we achieve
accelerated 3D rendering on a DisplayLink monitor. In another example
we funnel rendered pixels to userland by emulating a Video-for-Linux
device (and then userland can do whatever it wants with it). While
doing all this, GPU has no idea that we are doing this, the entire DRI
"thinks" that it is just dealing with a GPU that has a few "extra"
connectors and CRTCs. So everything ports without the need to modify
anything in the userland.
In general any device that can do something good with rendered pixels
can act as a CTD device, allowing a GPU to be an acceleration device
for a less capable display device or (the opposite) a frame-buffer-based
display device to be an expansion card for a GPU. Of course, for
each display device, a driver would have to be made compatible with our
new infrastructure (which is what we have done with DisplayLink driver
and also wrote one "synthetic" driver to fake out a V4L2 device as a
CTD device).
The newly introduced kernel module that we call VCRTCM (short for
Virtual CRTC Manager) handles the "traffic" between GPUs (actually
their CRTCs) and CTDs. The code makes use of DMA wherever possible
and also deals with specifics of CRTCs like modes, vblanks, page
flips, hardware cursor, etc. (just for kick, we played OpenArena
and watched Unigine Heaven demo on a DisplayLink monitor).
At this time, we would like to solicit feedback, comments, and
possibly contributions. The code is on github (pointers below)
and is based on the current state of drm-next branch from Dave's
tree. The code is very experimental, but complete and stable enough
that you can do something useful with it. We will be adding more
CTD drivers and updates to current ones in the near future and will
continue to maintain the code on github.
If the community finds this useful, we would be glad to work with
the maintainers on merging this upstream. So we would especially like
to hear what you would like to see changed to make this code acceptable
for the mainline of development.
My Github page is at https://github.com/ihadzic. To access the kernel
code type:
$ git clone git://github.com/ihadzic/linux-vcrtcm.git
$ git branch drm-next-vcrtcm origin/drm-next-vcrtcm
$ git checkout drm-next-vcrtcm
You will get all that's currently on Dave's drm-next plus our patches on
the top. We kept the development history preserved without squashing patches
(unless we had to due to merge/rebase conflicts), so you can see (and laugh
at) all our goofs and fixes to them.
To access the documentation, type:
$ git clone git://github.com/ihadzic/vcrtcm-doc.git
Then read the HOWTO.txt file. The first few sections provide some
general overview, and the sections that come later provide instructions
how to use our stuff.
Again, all comments, positive or negative, are very welcome.
-- Ilija
^ permalink raw reply
* RE: [PATCH] FSL/DIU: add 1080P, 720P and 800x600 video modes
From: Huang Changming-R66093 @ 2011-11-03 2:32 UTC (permalink / raw)
To: linux-fbdev
In-Reply-To: <1320215710-9128-1-git-send-email-r66093@freescale.com>
Please ignore this patch.
Thanks and Best Regards
Jerry Huang
> -----Original Message-----
> From: Huang Changming-R66093
> Sent: Wednesday, November 02, 2011 2:35 PM
> To: linux-fbdev@vger.kernel.org
> Cc: Huang Changming-R66093; Paul Mundt
> Subject: [PATCH] FSL/DIU: add 1080P, 720P and 800x600 video modes
>
> From: Jerry Huang <Chang-Ming.Huang@freescale.com>
>
> Add 1920x1080, 1280x720 and 800x600 resolution support.
>
> Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com>
> CC: Paul Mundt <lethal@linux-sh.org>
> ---
> drivers/video/fsl-diu-fb.c | 47
> +++++++++++++++++++++++++++++++++++++++++++-
> 1 files changed, 46 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
> index 0acc7d6..756975f 100644
> --- a/drivers/video/fsl-diu-fb.c
> +++ b/drivers/video/fsl-diu-fb.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
> + * Copyright 2008,2011 Freescale Semiconductor, Inc. All Rights
> Reserved.
> *
> * Freescale DIU Frame Buffer device driver
> *
> @@ -60,6 +60,21 @@ static struct fb_videomode __devinitdata
> fsl_diu_default_mode = {
>
> static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
> {
> + .name = "800x600-60",
> + .refresh = 60,
> + .xres = 800,
> + .yres = 600,
> + .pixclock = 25000,
> + .left_margin = 88,
> + .right_margin = 40,
> + .upper_margin = 23,
> + .lower_margin = 1,
> + .hsync_len = 128,
> + .vsync_len = 4,
> + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
> + .vmode = FB_VMODE_NONINTERLACED
> + },
> + {
> .name = "1024x768-60",
> .refresh = 60,
> .xres = 1024,
> @@ -105,6 +120,21 @@ static struct fb_videomode __devinitdata
> fsl_diu_mode_db[] = {
> .vmode = FB_VMODE_NONINTERLACED
> },
> {
> + .name = "1280x720-60",
> + .refresh = 60,
> + .xres = 1280,
> + .yres = 720,
> + .pixclock = 13426,
> + .left_margin = 192,
> + .right_margin = 64,
> + .upper_margin = 22,
> + .lower_margin = 1,
> + .hsync_len = 136,
> + .vsync_len = 3,
> + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
> + .vmode = FB_VMODE_NONINTERLACED
> + },
> + {
> .name = "1280x1024-60",
> .refresh = 60,
> .xres = 1280,
> @@ -150,6 +180,21 @@ static struct fb_videomode __devinitdata
> fsl_diu_mode_db[] = {
> .vmode = FB_VMODE_NONINTERLACED
> },
> {
> + .name = "1920x1080-60",
> + .refresh = 60,
> + .xres = 1920,
> + .yres = 1080,
> + .pixclock = 5787,
> + .left_margin = 328,
> + .right_margin = 120,
> + .upper_margin = 34,
> + .lower_margin = 1,
> + .hsync_len = 208,
> + .vsync_len = 3,
> + .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
> + .vmode = FB_VMODE_NONINTERLACED
> + },
> + {
> .name = "320x240", /* for AOI only */
> .refresh = 60,
> .xres = 320,
> --
> 1.7.4.1
^ permalink raw reply
* Re: [PATCH] FSL/DIU: add 1080P, 720P and 800x600 video modes
From: Tabi Timur-B04825 @ 2011-11-02 13:23 UTC (permalink / raw)
To: linux-fbdev
In-Reply-To: <1320215710-9128-1-git-send-email-r66093@freescale.com>
On Wed, Nov 2, 2011 at 1:35 AM, <r66093@freescale.com> wrote:
> From: Jerry Huang <Chang-Ming.Huang@freescale.com>
>
> Add 1920x1080, 1280x720 and 800x600 resolution support.
>
> Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com>
> CC: Paul Mundt <lethal@linux-sh.org>
> ---
These video modes are already in the driver.
--
Timur Tabi
Linux kernel developer at Freescale
^ permalink raw reply
* [PATCH] FSL/DIU: add 1080P, 720P and 800x600 video modes
From: r66093 @ 2011-11-02 6:35 UTC (permalink / raw)
To: linux-fbdev
From: Jerry Huang <Chang-Ming.Huang@freescale.com>
Add 1920x1080, 1280x720 and 800x600 resolution support.
Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com>
CC: Paul Mundt <lethal@linux-sh.org>
---
drivers/video/fsl-diu-fb.c | 47 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 46 insertions(+), 1 deletions(-)
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index 0acc7d6..756975f 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008,2011 Freescale Semiconductor, Inc. All Rights Reserved.
*
* Freescale DIU Frame Buffer device driver
*
@@ -60,6 +60,21 @@ static struct fb_videomode __devinitdata fsl_diu_default_mode = {
static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
{
+ .name = "800x600-60",
+ .refresh = 60,
+ .xres = 800,
+ .yres = 600,
+ .pixclock = 25000,
+ .left_margin = 88,
+ .right_margin = 40,
+ .upper_margin = 23,
+ .lower_margin = 1,
+ .hsync_len = 128,
+ .vsync_len = 4,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
.name = "1024x768-60",
.refresh = 60,
.xres = 1024,
@@ -105,6 +120,21 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
.vmode = FB_VMODE_NONINTERLACED
},
{
+ .name = "1280x720-60",
+ .refresh = 60,
+ .xres = 1280,
+ .yres = 720,
+ .pixclock = 13426,
+ .left_margin = 192,
+ .right_margin = 64,
+ .upper_margin = 22,
+ .lower_margin = 1,
+ .hsync_len = 136,
+ .vsync_len = 3,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
.name = "1280x1024-60",
.refresh = 60,
.xres = 1280,
@@ -150,6 +180,21 @@ static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
.vmode = FB_VMODE_NONINTERLACED
},
{
+ .name = "1920x1080-60",
+ .refresh = 60,
+ .xres = 1920,
+ .yres = 1080,
+ .pixclock = 5787,
+ .left_margin = 328,
+ .right_margin = 120,
+ .upper_margin = 34,
+ .lower_margin = 1,
+ .hsync_len = 208,
+ .vsync_len = 3,
+ .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ {
.name = "320x240", /* for AOI only */
.refresh = 60,
.xres = 320,
--
1.7.4.1
^ permalink raw reply related
* [PATCH 09/18] MIPS: Alchemy: move au1200fb global functions to platform data
From: Manuel Lauss @ 2011-11-01 19:03 UTC (permalink / raw)
To: Linux-MIPS, Ralf Baechle; +Cc: Manuel Lauss, linux-fbdev
In-Reply-To: <1320174224-27305-1-git-send-email-manuel.lauss@googlemail.com>
au1200fb calls 3 functions which have to be defined in board code.
Fix this ugliness with the introduction of platform_data.
Cc: linux-fbdev@vger.kernel.org
Signed-off-by: Manuel Lauss <manuel.lauss@googlemail.com>
---
I'd like this to go in via the MIPS tree since patches depend on it.
arch/mips/alchemy/devboards/db1200.c | 51 +++---
arch/mips/alchemy/devboards/db1300.c | 52 +++---
arch/mips/alchemy/devboards/pb1200.c | 55 ++++---
arch/mips/include/asm/mach-au1x00/au1200fb.h | 14 ++
drivers/video/au1200fb.c | 241 ++++++++++++--------------
5 files changed, 213 insertions(+), 200 deletions(-)
create mode 100644 arch/mips/include/asm/mach-au1x00/au1200fb.h
diff --git a/arch/mips/alchemy/devboards/db1200.c b/arch/mips/alchemy/devboards/db1200.c
index 43f5f1b..e2cc5f9 100644
--- a/arch/mips/alchemy/devboards/db1200.c
+++ b/arch/mips/alchemy/devboards/db1200.c
@@ -37,6 +37,7 @@
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1100_mmc.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
+#include <asm/mach-au1x00/au1200fb.h>
#include <asm/mach-au1x00/au1550_spi.h>
#include <asm/mach-db1x00/bcsr.h>
#include <asm/mach-db1x00/db1200.h>
@@ -422,6 +423,33 @@ static struct platform_device db1200_mmc0_dev = {
/**********************************************************************/
+static int db1200fb_panel_index(void)
+{
+ return (bcsr_read(BCSR_SWITCHES) >> 8) & 0x0f;
+}
+
+static int db1200fb_panel_init(void)
+{
+ /* Apply power */
+ bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
+ BCSR_BOARD_LCDBL);
+ return 0;
+}
+
+static int db1200fb_panel_shutdown(void)
+{
+ /* Remove power */
+ bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
+ BCSR_BOARD_LCDBL, 0);
+ return 0;
+}
+
+static struct au1200fb_platdata db1200fb_pd = {
+ .panel_index = db1200fb_panel_index,
+ .panel_init = db1200fb_panel_init,
+ .panel_shutdown = db1200fb_panel_shutdown,
+};
+
static struct resource au1200_lcd_res[] = {
[0] = {
.start = AU1200_LCD_PHYS_ADDR,
@@ -443,6 +471,7 @@ static struct platform_device au1200_lcd_dev = {
.dev = {
.dma_mask = &au1200_lcd_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &db1200fb_pd,
},
.num_resources = ARRAY_SIZE(au1200_lcd_res),
.resource = au1200_lcd_res,
@@ -681,25 +710,3 @@ static int __init db1200_dev_init(void)
return platform_add_devices(db1200_devs, ARRAY_SIZE(db1200_devs));
}
device_initcall(db1200_dev_init);
-
-/* au1200fb calls these: STERBT EINEN TRAGISCHEN TOD!!! */
-int board_au1200fb_panel(void)
-{
- return (bcsr_read(BCSR_SWITCHES) >> 8) & 0x0f;
-}
-
-int board_au1200fb_panel_init(void)
-{
- /* Apply power */
- bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
- BCSR_BOARD_LCDBL);
- return 0;
-}
-
-int board_au1200fb_panel_shutdown(void)
-{
- /* Remove power */
- bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
- BCSR_BOARD_LCDBL, 0);
- return 0;
-}
diff --git a/arch/mips/alchemy/devboards/db1300.c b/arch/mips/alchemy/devboards/db1300.c
index bf0d1c4..d1a7344 100644
--- a/arch/mips/alchemy/devboards/db1300.c
+++ b/arch/mips/alchemy/devboards/db1300.c
@@ -22,6 +22,7 @@
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1100_mmc.h>
+#include <asm/mach-au1x00/au1200fb.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
#include <asm/mach-db1x00/db1300.h>
@@ -635,6 +636,33 @@ static struct platform_device db1300_sndi2s_dev = {
/**********************************************************************/
+static int db1300fb_panel_index(void)
+{
+ return 9; /* DB1300_800x480 */
+}
+
+static int db1300fb_panel_init(void)
+{
+ /* Apply power (Vee/Vdd logic is inverted on Panel DB1300_800x480) */
+ bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD,
+ BCSR_BOARD_LCDBL);
+ return 0;
+}
+
+static int db1300fb_panel_shutdown(void)
+{
+ /* Remove power (Vee/Vdd logic is inverted on Panel DB1300_800x480) */
+ bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDBL,
+ BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD);
+ return 0;
+}
+
+static struct au1200fb_platdata db1300fb_pd = {
+ .panel_index = db1300fb_panel_index,
+ .panel_init = db1300fb_panel_init,
+ .panel_shutdown = db1300fb_panel_shutdown,
+};
+
static struct resource au1300_lcd_res[] = {
[0] = {
.start = AU1200_LCD_PHYS_ADDR,
@@ -656,6 +684,7 @@ static struct platform_device db1300_lcd_dev = {
.dev = {
.dma_mask = &au1300_lcd_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &db1300fb_pd,
},
.num_resources = ARRAY_SIZE(au1300_lcd_res),
.resource = au1300_lcd_res,
@@ -761,26 +790,3 @@ void __init board_setup(void)
alchemy_uart_enable(AU1300_UART1_PHYS_ADDR);
alchemy_uart_enable(AU1300_UART3_PHYS_ADDR);
}
-
-
-/* au1200fb calls these: STERBT EINEN TRAGISCHEN TOD!!! */
-int board_au1200fb_panel(void)
-{
- return 9; /* DB1300_800x480 */
-}
-
-int board_au1200fb_panel_init(void)
-{
- /* Apply power (Vee/Vdd logic is inverted on Panel DB1300_800x480) */
- bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD,
- BCSR_BOARD_LCDBL);
- return 0;
-}
-
-int board_au1200fb_panel_shutdown(void)
-{
- /* Remove power (Vee/Vdd logic is inverted on Panel DB1300_800x480) */
- bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDBL,
- BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD);
- return 0;
-}
diff --git a/arch/mips/alchemy/devboards/pb1200.c b/arch/mips/alchemy/devboards/pb1200.c
index a1b6497..a2676c9 100644
--- a/arch/mips/alchemy/devboards/pb1200.c
+++ b/arch/mips/alchemy/devboards/pb1200.c
@@ -26,6 +26,7 @@
#include <linux/smc91x.h>
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1100_mmc.h>
+#include <asm/mach-au1x00/au1200fb.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
#include <asm/mach-db1x00/bcsr.h>
#include <asm/mach-pb1x00/pb1200.h>
@@ -351,6 +352,33 @@ static struct platform_device pb1200_i2c_dev = {
.resource = au1200_psc0_res,
};
+static int pb1200fb_panel_index(void)
+{
+ return (bcsr_read(BCSR_SWITCHES) >> 8) & 0x0f;
+}
+
+static int pb1200fb_panel_init(void)
+{
+ /* Apply power */
+ bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
+ BCSR_BOARD_LCDBL);
+ return 0;
+}
+
+static int pb1200fb_panel_shutdown(void)
+{
+ /* Remove power */
+ bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
+ BCSR_BOARD_LCDBL, 0);
+ return 0;
+}
+
+static struct au1200fb_platdata pb1200fb_pd = {
+ .panel_index = pb1200fb_panel_index,
+ .panel_init = pb1200fb_panel_init,
+ .panel_shutdown = pb1200fb_panel_shutdown,
+};
+
static struct resource au1200_lcd_res[] = {
[0] = {
.start = AU1200_LCD_PHYS_ADDR,
@@ -366,12 +394,13 @@ static struct resource au1200_lcd_res[] = {
static u64 au1200_lcd_dmamask = DMA_BIT_MASK(32);
-static struct platform_device au1200_lcd_dev = {
+static struct platform_device pb1200_lcd_dev = {
.name = "au1200-lcd",
.id = 0,
.dev = {
.dma_mask = &au1200_lcd_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &pb1200fb_pd,
},
.num_resources = ARRAY_SIZE(au1200_lcd_res),
.resource = au1200_lcd_res,
@@ -383,7 +412,7 @@ static struct platform_device *board_platform_devices[] __initdata = {
&pb1200_i2c_dev,
&pb1200_mmc0_dev,
&pb1200_mmc1_dev,
- &au1200_lcd_dev,
+ &pb1200_lcd_dev,
};
static int __init board_register_devices(void)
@@ -440,25 +469,3 @@ static int __init board_register_devices(void)
ARRAY_SIZE(board_platform_devices));
}
device_initcall(board_register_devices);
-
-
-int board_au1200fb_panel(void)
-{
- return (bcsr_read(BCSR_SWITCHES) >> 8) & 0x0f;
-}
-
-int board_au1200fb_panel_init(void)
-{
- /* Apply power */
- bcsr_mod(BCSR_BOARD, 0, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
- BCSR_BOARD_LCDBL);
- return 0;
-}
-
-int board_au1200fb_panel_shutdown(void)
-{
- /* Remove power */
- bcsr_mod(BCSR_BOARD, BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD |
- BCSR_BOARD_LCDBL, 0);
- return 0;
-}
diff --git a/arch/mips/include/asm/mach-au1x00/au1200fb.h b/arch/mips/include/asm/mach-au1x00/au1200fb.h
new file mode 100644
index 0000000..b3c87cc
--- /dev/null
+++ b/arch/mips/include/asm/mach-au1x00/au1200fb.h
@@ -0,0 +1,14 @@
+/*
+ * platform data for au1200fb driver.
+ */
+
+#ifndef _AU1200FB_PLAT_H_
+#define _AU1200FB_PLAT_H_
+
+struct au1200fb_platdata {
+ int (*panel_index)(void);
+ int (*panel_init)(void);
+ int (*panel_shutdown)(void);
+};
+
+#endif
diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c
index 6c4342f..04e4479 100644
--- a/drivers/video/au1200fb.c
+++ b/drivers/video/au1200fb.c
@@ -44,6 +44,7 @@
#include <linux/slab.h>
#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1200fb.h> /* platform_data */
#include "au1200fb.h"
#define DRIVER_NAME "au1200fb"
@@ -143,6 +144,7 @@ struct au1200_lcd_iodata_t {
/* Private, per-framebuffer management information (independent of the panel itself) */
struct au1200fb_device {
struct fb_info *fb_info; /* FB driver info record */
+ struct au1200fb_platdata *pd;
int plane;
unsigned char* fb_mem; /* FrameBuffer memory map */
@@ -201,9 +203,6 @@ struct window_settings {
#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_01
#endif
-extern int board_au1200fb_panel_init (void);
-extern int board_au1200fb_panel_shutdown (void);
-
/*
* Default window configurations
*/
@@ -334,8 +333,6 @@ struct panel_settings
uint32 mode_toyclksrc;
uint32 mode_backlight;
uint32 mode_auxpll;
- int (*device_init)(void);
- int (*device_shutdown)(void);
#define Xres min_xres
#define Yres min_yres
u32 min_xres; /* Minimum horizontal resolution */
@@ -385,8 +382,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 8, /* 96MHz AUXPLL */
- .device_init = NULL,
- .device_shutdown = NULL,
320, 320,
240, 240,
},
@@ -415,8 +410,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 8, /* 96MHz AUXPLL */
- .device_init = NULL,
- .device_shutdown = NULL,
640, 480,
640, 480,
},
@@ -445,8 +438,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 8, /* 96MHz AUXPLL */
- .device_init = NULL,
- .device_shutdown = NULL,
800, 800,
600, 600,
},
@@ -475,8 +466,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 6, /* 72MHz AUXPLL */
- .device_init = NULL,
- .device_shutdown = NULL,
1024, 1024,
768, 768,
},
@@ -505,8 +494,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 10, /* 120MHz AUXPLL */
- .device_init = NULL,
- .device_shutdown = NULL,
1280, 1280,
1024, 1024,
},
@@ -535,8 +522,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 8, /* 96MHz AUXPLL */
- .device_init = board_au1200fb_panel_init,
- .device_shutdown = board_au1200fb_panel_shutdown,
1024, 1024,
768, 768,
},
@@ -568,8 +553,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 8, /* 96MHz AUXPLL */
- .device_init = board_au1200fb_panel_init,
- .device_shutdown = board_au1200fb_panel_shutdown,
640, 480,
640, 480,
},
@@ -601,8 +584,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 8, /* 96MHz AUXPLL */
- .device_init = board_au1200fb_panel_init,
- .device_shutdown = board_au1200fb_panel_shutdown,
320, 320,
240, 240,
},
@@ -634,8 +615,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = 8, /* 96MHz AUXPLL */
- .device_init = board_au1200fb_panel_init,
- .device_shutdown = board_au1200fb_panel_shutdown,
856, 856,
480, 480,
},
@@ -670,8 +649,6 @@ static struct panel_settings known_lcd_panels[] .mode_toyclksrc = 0x00000004, /* AUXPLL directly */
.mode_backlight = 0x00000000,
.mode_auxpll = (48/12) * 2,
- .device_init = board_au1200fb_panel_init,
- .device_shutdown = board_au1200fb_panel_shutdown,
800, 800,
480, 480,
},
@@ -800,7 +777,8 @@ static int au1200_setlocation (struct au1200fb_device *fbdev, int plane,
return 0;
}
-static void au1200_setpanel (struct panel_settings *newpanel)
+static void au1200_setpanel(struct panel_settings *newpanel,
+ struct au1200fb_platdata *pd)
{
/*
* Perform global setup/init of LCD controller
@@ -834,8 +812,8 @@ static void au1200_setpanel (struct panel_settings *newpanel)
the controller, the clock cannot be turned off before first
shutting down the controller.
*/
- if (panel->device_shutdown != NULL)
- panel->device_shutdown();
+ if (pd->panel_shutdown)
+ pd->panel_shutdown();
}
/* Newpanel = NULL indicates a shutdown operation only */
@@ -888,7 +866,8 @@ static void au1200_setpanel (struct panel_settings *newpanel)
au_sync();
/* Call init of panel */
- if (panel->device_init != NULL) panel->device_init();
+ if (pd->panel_init)
+ pd->panel_init();
/* FIX!!!! not appropriate on panel change!!! Global setup/init */
lcd->intenable = 0;
@@ -1221,6 +1200,8 @@ static int au1200fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
*/
static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi)
{
+ struct au1200fb_device *fbdev = fbi->par;
+
/* Short-circuit screen blanking */
if (noblanking)
return 0;
@@ -1230,13 +1211,13 @@ static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi)
case FB_BLANK_UNBLANK:
case FB_BLANK_NORMAL:
/* printk("turn on panel\n"); */
- au1200_setpanel(panel);
+ au1200_setpanel(panel, fbdev->pd);
break;
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_POWERDOWN:
/* printk("turn off panel\n"); */
- au1200_setpanel(NULL);
+ au1200_setpanel(NULL, fbdev->pd);
break;
default:
break;
@@ -1464,6 +1445,7 @@ static void get_window(unsigned int plane,
static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
+ struct au1200fb_device *fbdev = info->par;
int plane;
int val;
@@ -1508,7 +1490,7 @@ static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd,
struct panel_settings *newpanel;
panel_index = iodata.global.panel_choice;
newpanel = &known_lcd_panels[panel_index];
- au1200_setpanel(newpanel);
+ au1200_setpanel(newpanel, fbdev->pd);
}
break;
@@ -1624,22 +1606,102 @@ static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev)
/*-------------------------------------------------------------------------*/
-/* AU1200 LCD controller device driver */
+static int au1200fb_setup(struct au1200fb_platdata *pd)
+{
+ char *options = NULL;
+ char *this_opt, *endptr;
+ int num_panels = ARRAY_SIZE(known_lcd_panels);
+ int panel_idx = -1;
+
+ fb_get_options(DRIVER_NAME, &options);
+
+ if (!options)
+ goto out;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ /* Panel option - can be panel name,
+ * "bs" for board-switch, or number/index */
+ if (!strncmp(this_opt, "panel:", 6)) {
+ int i;
+ long int li;
+ char *endptr;
+ this_opt += 6;
+ /* First check for index, which allows
+ * to short circuit this mess */
+ li = simple_strtol(this_opt, &endptr, 0);
+ if (*endptr = '\0')
+ panel_idx = (int)li;
+ else if (strcmp(this_opt, "bs") = 0)
+ panel_idx = pd->panel_index();
+ else {
+ for (i = 0; i < num_panels; i++) {
+ if (!strcmp(this_opt,
+ known_lcd_panels[i].name)) {
+ panel_idx = i;
+ break;
+ }
+ }
+ }
+ if ((panel_idx < 0) || (panel_idx >= num_panels))
+ print_warn("Panel %s not supported!", this_opt);
+ else
+ panel_index = panel_idx;
+
+ } else if (strncmp(this_opt, "nohwcursor", 10) = 0)
+ nohwcursor = 1;
+ else if (strncmp(this_opt, "devices:", 8) = 0) {
+ this_opt += 8;
+ device_count = simple_strtol(this_opt, &endptr, 0);
+ if ((device_count < 0) ||
+ (device_count > MAX_DEVICE_COUNT))
+ device_count = MAX_DEVICE_COUNT;
+ } else if (strncmp(this_opt, "wincfg:", 7) = 0) {
+ this_opt += 7;
+ window_index = simple_strtol(this_opt, &endptr, 0);
+ if ((window_index < 0) ||
+ (window_index >= ARRAY_SIZE(windows)))
+ window_index = DEFAULT_WINDOW_INDEX;
+ } else if (strncmp(this_opt, "off", 3) = 0)
+ return 1;
+ else
+ print_warn("Unsupported option \"%s\"", this_opt);
+ }
+
+out:
+ return 0;
+}
+
+/* AU1200 LCD controller device driver */
static int __devinit au1200fb_drv_probe(struct platform_device *dev)
{
struct au1200fb_device *fbdev;
+ struct au1200fb_platdata *pd;
struct fb_info *fbi = NULL;
unsigned long page;
int bpp, plane, ret, irq;
+ print_info("" DRIVER_DESC "");
+
+ pd = dev->dev.platform_data;
+ if (!pd)
+ return -ENODEV;
+
+ /* Setup driver with options */
+ if (au1200fb_setup(pd))
+ return -ENODEV;
+
+ /* Point to the panel selected */
+ panel = &known_lcd_panels[panel_index];
+ win = &windows[window_index];
+
+ printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name);
+ printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name);
+
/* shut gcc up */
ret = 0;
fbdev = NULL;
- /* Kickstart the panel */
- au1200_setpanel(panel);
-
for (plane = 0; plane < device_count; ++plane) {
bpp = winbpp(win->w[plane].mode_winctrl1);
if (win->w[plane].xres = 0)
@@ -1655,6 +1717,7 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev)
_au1200fb_infos[plane] = fbi;
fbdev = fbi->par;
fbdev->fb_info = fbi;
+ fbdev->pd = pd;
fbdev->plane = plane;
@@ -1716,6 +1779,11 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev)
goto failed;
}
+ platform_set_drvdata(dev, pd);
+
+ /* Kickstart the panel */
+ au1200_setpanel(panel, pd);
+
return 0;
failed:
@@ -1735,12 +1803,13 @@ failed:
static int __devexit au1200fb_drv_remove(struct platform_device *dev)
{
+ struct au1200fb_platdata *pd = platform_get_drvdata(dev);
struct au1200fb_device *fbdev;
struct fb_info *fbi;
int plane;
/* Turn off the panel */
- au1200_setpanel(NULL);
+ au1200_setpanel(NULL, pd);
for (plane = 0; plane < device_count; ++plane) {
fbi = _au1200fb_infos[plane];
@@ -1768,7 +1837,8 @@ static int __devexit au1200fb_drv_remove(struct platform_device *dev)
#ifdef CONFIG_PM
static int au1200fb_drv_suspend(struct device *dev)
{
- au1200_setpanel(NULL);
+ struct au1200fb_platdata *pd = dev_get_drvdata(dev);
+ au1200_setpanel(NULL, pd);
lcd->outmask = 0;
au_sync();
@@ -1778,11 +1848,12 @@ static int au1200fb_drv_suspend(struct device *dev)
static int au1200fb_drv_resume(struct device *dev)
{
+ struct au1200fb_platdata *pd = dev_get_drvdata(dev);
struct fb_info *fbi;
int i;
/* Kickstart the panel */
- au1200_setpanel(panel);
+ au1200_setpanel(panel, pd);
for (i = 0; i < device_count; i++) {
fbi = _au1200fb_infos[i];
@@ -1817,100 +1888,8 @@ static struct platform_driver au1200fb_driver = {
/*-------------------------------------------------------------------------*/
-/* Kernel driver */
-
-static int au1200fb_setup(void)
-{
- char *options = NULL;
- char *this_opt, *endptr;
- int num_panels = ARRAY_SIZE(known_lcd_panels);
- int panel_idx = -1;
-
- fb_get_options(DRIVER_NAME, &options);
-
- if (options) {
- while ((this_opt = strsep(&options,",")) != NULL) {
- /* Panel option - can be panel name,
- * "bs" for board-switch, or number/index */
- if (!strncmp(this_opt, "panel:", 6)) {
- int i;
- long int li;
- char *endptr;
- this_opt += 6;
- /* First check for index, which allows
- * to short circuit this mess */
- li = simple_strtol(this_opt, &endptr, 0);
- if (*endptr = '\0') {
- panel_idx = (int)li;
- }
- else if (strcmp(this_opt, "bs") = 0) {
- extern int board_au1200fb_panel(void);
- panel_idx = board_au1200fb_panel();
- }
-
- else
- for (i = 0; i < num_panels; i++) {
- if (!strcmp(this_opt, known_lcd_panels[i].name)) {
- panel_idx = i;
- break;
- }
- }
-
- if ((panel_idx < 0) || (panel_idx >= num_panels)) {
- print_warn("Panel %s not supported!", this_opt);
- }
- else
- panel_index = panel_idx;
- }
-
- else if (strncmp(this_opt, "nohwcursor", 10) = 0) {
- nohwcursor = 1;
- }
-
- else if (strncmp(this_opt, "devices:", 8) = 0) {
- this_opt += 8;
- device_count = simple_strtol(this_opt,
- &endptr, 0);
- if ((device_count < 0) ||
- (device_count > MAX_DEVICE_COUNT))
- device_count = MAX_DEVICE_COUNT;
- }
-
- else if (strncmp(this_opt, "wincfg:", 7) = 0) {
- this_opt += 7;
- window_index = simple_strtol(this_opt,
- &endptr, 0);
- if ((window_index < 0) ||
- (window_index >= ARRAY_SIZE(windows)))
- window_index = DEFAULT_WINDOW_INDEX;
- }
-
- else if (strncmp(this_opt, "off", 3) = 0)
- return 1;
- /* Unsupported option */
- else {
- print_warn("Unsupported option \"%s\"", this_opt);
- }
- }
- }
- return 0;
-}
-
static int __init au1200fb_init(void)
{
- print_info("" DRIVER_DESC "");
-
- /* Setup driver with options */
- if (au1200fb_setup())
- return -ENODEV;
-
- /* Point to the panel selected */
- panel = &known_lcd_panels[panel_index];
- win = &windows[window_index];
-
- printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name);
- printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name);
-
return platform_driver_register(&au1200fb_driver);
}
--
1.7.7.1
^ permalink raw reply related
* Re: [PATCH v2] GIO bus support for SGI IP22/28
From: Thomas Bogendoerfer @ 2011-11-01 10:00 UTC (permalink / raw)
To: Joshua Kinard; +Cc: linux-mips, linux-fbdev, ralf, FlorianSchandinat
In-Reply-To: <4EAE5681.2090103@gentoo.org>
On Mon, Oct 31, 2011 at 04:04:17AM -0400, Joshua Kinard wrote:
> A.k.a., Tulip (and possibly ThunderLAN) assume little-endian, when we're
> talking big-endian archs here. Interesting. Simple fix, as in defining a
> few driver structures with little- and big-endian versions (if they're doing
> something like packing bits or using bitfields)? Or is it more complex than
> that?
iirc, the PCI bridge on the cards do 32bit swaps (address invariant) so
tulip chips need to run in big endian mode, which in turn makes descriptors
also big endian. But none of the tulip driver supports big endian descriptors.
That's fixable of course.
Thomas.
--
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
good idea. [ RFC1925, 2.3 ]
^ permalink raw reply
* Re: Proposal for a low-level Linux display framework
From: Jesse Barnes @ 2011-10-31 20:24 UTC (permalink / raw)
To: Alan Cox
Cc: Dave Airlie, linux-fbdev, linaro-dev, Florian Tobias Schandinat,
linux-kernel, dri-devel, Archit Taneja, Rob Clark
In-Reply-To: <20110917212529.6b452bf2@lxorguk.ukuu.org.uk>
[-- Attachment #1: Type: text/plain, Size: 3068 bytes --]
On Sat, 17 Sep 2011 21:25:29 +0100
Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
> > Just tell the X driver to not use acceleration, and it you won't get
> > any acceleration used, then you get complete stability. If a driver
> > writer wants to turn off all accel in the kernel driver, it can, its
>
> In fact one thing we actually need really is a "dumb" KMS X server to
> replace the fbdev X server that unaccel stuff depends upon and which
> can't do proper mode handling, multi-head or resizing as a result. A dumb
> fb generic request for a back to front copy might also be useful for
> shadowfb, or at least indicators so you know what the cache behaviour is
> so the X server can pick the right policy.
>
> > We've fixed this in KMS, we don't pass direct mappings to userspace
> > that we can't tear down and refault. We only provide objects via
> > handles. The only place its a problem is where we expose fbdev legacy
> > emulation, since we have to fix the pages.
>
> Which is doable. Horrible but doable. The usb framebuffer code has to
> play games like this with the virtual framebuffer in order to track
> changes by faulting.
>
> There are still some architectural screwups however. DRM continues the
> fbdev worldview that outputs, memory and accelerators are tied together
> in lumps we call video cards. That isn't really true for all cases and
> with capture/overlay it gets even less true.
Sorry for re-opening this ancient thread; I'm catching up from the past
2 months of travel & misc.
I definitely agree about the PC card centric architecture of DRM KMS
(and before it, X). But we have a path out of it now, and lots of
interest from vendors and developers, so I don't think it's an
insurmountable problem by any means.
I definitely understand Florian's worries about DRM vs fb. If nothing
else, there's certainly a perception that fb is simpler and easier to
get right. But really, as others have pointed out, it's solving a
different set of problems than the DRM layer. The latter is actually
trying to expose the features of contemporary hardware in a way that's
as portable as possible. That portability comes at a cost though: the
APIs we add need to get lots of review, and there's no doubt we'll need
to add more as newer, weirder hardware comes along.
Really, I see no reason why fb and DRM can't continue to live side by
side. If a vendor really only needs the features provided by the fb
layer, they're free to stick with a simple fb driver. However, I
expect most vendors making phones, tablets, notebooks, etc will need
and want an architecture that looks a lot like the DRM layer, with
authentication for rendering clients, an command submission ioctl for
acceleration, and memory management, so I expect most of the driver
growth to be in DRM in the near future.
And I totally agree with Dave about having a kmscon. I really wish
someone would implement it so I could have my VTs spinning on a cube.
--
Jesse Barnes, Intel Open Source Technology Center
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v2] GIO bus support for SGI IP22/28
From: Joshua Kinard @ 2011-10-31 8:04 UTC (permalink / raw)
To: Thomas Bogendoerfer; +Cc: linux-mips, linux-fbdev, ralf, FlorianSchandinat
In-Reply-To: <20111030223418.GA16346@alpha.franken.de>
On 10/30/2011 18:34, Thomas Bogendoerfer wrote:
>
> no, but it will make live a lot easier, because address and interrupts don't
> need to be probed by the driver. Right now interrupts are on my todo, since
> there is some weirdness between guiness and fullhouse boxes...
It wouldn't be an SGI machine if it didn't implement something weird or just
plain backwards...
> it still needs something to setup the PCI bus on the card and issue
> the probing. The problem with the Tulip Phobos cards is, that they
> messed up the endianess, so that none of the Linux Tulip drivers will
> work out of the box...
A.k.a., Tulip (and possibly ThunderLAN) assume little-endian, when we're
talking big-endian archs here. Interesting. Simple fix, as in defining a
few driver structures with little- and big-endian versions (if they're doing
something like packing bits or using bitfields)? Or is it more complex than
that?
--
Joshua Kinard
Gentoo/MIPS
kumba@gentoo.org
4096R/D25D95E3 2011-03-28
"The past tempts us, the present confuses us, the future frightens us. And
our lives slip away, moment by moment, lost in that vast, terrible in-between."
--Emperor Turhan, Centauri Republic
^ permalink raw reply
* Re: [PATCH v2] GIO bus support for SGI IP22/28
From: Thomas Bogendoerfer @ 2011-10-30 22:34 UTC (permalink / raw)
To: Joshua Kinard; +Cc: linux-mips, linux-fbdev, ralf, FlorianSchandinat
In-Reply-To: <4EADB701.9040506@gentoo.org>
On Sun, Oct 30, 2011 at 04:43:45PM -0400, Joshua Kinard wrote:
> Does this handle any glue logic for add-on NIC cards found for Indy and I2?
no, but it will make live a lot easier, because address and interrupts don't
need to be probed by the driver. Right now interrupts are on my todo, since
there is some weirdness between guiness and fullhouse boxes...
> I have a G130 Phobus and a rare ThunderLAN card in my Indy. The Phobus has
> an Altera GIO/PCI glue chip. Not sure about the ThunderLAN. Both have
> normal driver support in the kernel (Phobus is just a Tulip chip).
it still needs something to setup the PCI bus on the card and issue
the probing. The problem with the Tulip Phobos cards is, that they
messed up the endianess, so that none of the Linux Tulip drivers will
work out of the box...
Thomas.
--
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
good idea. [ RFC1925, 2.3 ]
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox