From 008a6926f361addc33ae188dbb230126832a04bd Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 20 Oct 2010 16:48:18 -0700 Subject: [PATCH 1/8] [ARM] tegra: dma: make checkpatch happy Signed-off-by: Iliyan Malchev --- arch/arm/mach-tegra/dma.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index b2f2b0fb681e..8da59aa855ff 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -365,7 +365,8 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) channel = find_first_zero_bit(channel_usage, ARRAY_SIZE(dma_channels)); if (channel >= ARRAY_SIZE(dma_channels)) { - pr_err("%s: failed to allocate a DMA channel",__func__); + pr_err("%s: failed to allocate a DMA channel", + __func__); goto out; } } @@ -574,18 +575,20 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch) if (req) { if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_EMPTY) { bool is_dma_ping_complete; - is_dma_ping_complete = (readl(ch->addr + APB_DMA_CHAN_STA) - & STA_PING_PONG) ? true : false; - if( req->to_memory ) + is_dma_ping_complete = + !!(readl(ch->addr + APB_DMA_CHAN_STA) & + STA_PING_PONG); + if (req->to_memory) is_dma_ping_complete = !is_dma_ping_complete; /* Out of sync - Release current buffer */ - if( !is_dma_ping_complete ) { + if (!is_dma_ping_complete) { int bytes_transferred; bytes_transferred = ch->req_transfer_count; bytes_transferred += 1; bytes_transferred <<= 3; - req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL; + req->buffer_status = + TEGRA_DMA_REQ_BUF_STATUS_FULL; req->bytes_transferred = bytes_transferred; req->status = TEGRA_DMA_REQ_SUCCESS; tegra_dma_stop(ch); @@ -598,13 +601,14 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch) list_del(&req->node); - /* DMA lock is NOT held when callbak is called */ + /* DMA lock is NOT held when callbak is + * called. */ spin_unlock_irqrestore(&ch->lock, irq_flags); req->complete(req); return; } - /* Load the next request into the hardware, if available - * */ + /* Load the next request into the hardware, if + * available. */ if (!list_is_last(&req->node, &ch->list)) { next_req = list_entry(req->node.next, typeof(*next_req), node); @@ -637,8 +641,10 @@ static void handle_continuous_dma(struct tegra_dma_channel *ch) /* It may be possible that req came after * half dma complete so it need to start * immediately */ - next_req = list_entry(req->node.next, typeof(*next_req), node); - if (next_req->status != TEGRA_DMA_REQ_INFLIGHT) { + next_req = list_entry(req->node.next, + typeof(*next_req), node); + if (next_req->status != + TEGRA_DMA_REQ_INFLIGHT) { tegra_dma_stop(ch); tegra_dma_update_hw(ch, next_req); } From f18c9f31a0d25535c13f0b663fedeb253370b72d Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Thu, 21 Oct 2010 16:48:37 -0700 Subject: [PATCH 2/8] [ARM] tegra: dma: expose TEGRA_DMA_MAX_TRANSFER_SIZE, fix typo NV_DMA_MAX_TRASFER_SIZE --> TEGRA_DMA_MAX_TRANSFER_SIZE Signed-off-by: Iliyan Malchev --- arch/arm/mach-tegra/dma.c | 4 +--- arch/arm/mach-tegra/include/mach/dma.h | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index 8da59aa855ff..ffba4441a99b 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -101,8 +101,6 @@ #define TEGRA_SYSTEM_DMA_CH_MAX \ (TEGRA_SYSTEM_DMA_CH_NR - TEGRA_SYSTEM_DMA_AVP_CH_NUM - 1) -#define NV_DMA_MAX_TRASFER_SIZE 0x10000 - const unsigned int ahb_addr_wrap_table[8] = { 0, 32, 64, 128, 256, 512, 1024, 2048 }; @@ -326,7 +324,7 @@ int tegra_dma_enqueue_req(struct tegra_dma_channel *ch, unsigned long irq_flags; int start_dma = 0; - if (req->size > NV_DMA_MAX_TRASFER_SIZE || + if (req->size > TEGRA_DMA_MAX_TRANSFER_SIZE || req->source_addr & 0x3 || req->dest_addr & 0x3) { pr_err("Invalid DMA request for channel %d\n", ch->id); return -EINVAL; diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h index 81e62782bac7..243050693eec 100644 --- a/arch/arm/mach-tegra/include/mach/dma.h +++ b/arch/arm/mach-tegra/include/mach/dma.h @@ -56,6 +56,8 @@ struct tegra_dma_channel; #define TEGRA_DMA_REQ_SEL_OWR 25 #define TEGRA_DMA_REQ_SEL_INVALID 31 +#define TEGRA_DMA_MAX_TRANSFER_SIZE 0x10000 + enum tegra_dma_mode { TEGRA_DMA_SHARED = 1, TEGRA_DMA_MODE_CONTINUOUS = 2, From ff7dc25c7f8644c8db0677d73b350640b5111c21 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 26 Oct 2010 17:32:13 -0700 Subject: [PATCH 3/8] ARM: tegra: suspend: Save and restore PLLP in low-level suspend Save and restore the PLLP registers in the platform suspend code, as the CPU clock may be sourced from the PLLP registers later, before the clock resume that used to re-enable PLLP has been called. Change-Id: I0ffc18d8a7f2d62c544328bd44ca7cf62848bc44 Signed-off-by: Colin Cross --- arch/arm/mach-tegra/headsmp-t2.S | 41 +++++++++++++++++++++++--------- arch/arm/mach-tegra/suspend.c | 31 +++++++++++++++++------- 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/arch/arm/mach-tegra/headsmp-t2.S b/arch/arm/mach-tegra/headsmp-t2.S index e25a3597f7fc..9da0ed68e63d 100644 --- a/arch/arm/mach-tegra/headsmp-t2.S +++ b/arch/arm/mach-tegra/headsmp-t2.S @@ -41,6 +41,13 @@ #define PMC_SCRATCH39 0x138 #define RST_DEVICES_U 0xc +#define CLK_RESET_PLLX_BASE 0xe0 +#define CLK_RESET_PLLX_MISC 0xe4 +#define CLK_RESET_PLLP_BASE 0xa0 +#define CLK_RESET_PLLP_OUTA 0xa4 +#define CLK_RESET_PLLP_OUTB 0xa8 +#define CLK_RESET_PLLP_MISC 0xac + /* .section ".cpuinit.text", "ax"*/ .macro poke_ev, val, tmp @@ -66,26 +73,38 @@ ENDPROC(tegra_secondary_startup) #endif /* - * __restart_pllx + * __restart_plls * - * Loads the saved PLLX parameters from tegra_sctx into PLLX, to - * allow it to stabilize while the rest of the CPU state is restored. + * Loads the saved PLLX and PLLP parameters into the PLLs, to + * allow them to stabilize while the rest of the CPU state is restored. * Should be called after the MMU is enabled. Jumps directly * to __cortex_a9_restore */ .align L1_CACHE_SHIFT -__restart_pllx: +__restart_plls: mov32 r0, tegra_sctx - ldr r1, [r0, #0x8] @ pllx_base - ldr r2, [r0, #0xC] @ pllx_misc mov32 r3, (TEGRA_CLK_RESET_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT) mov32 r4, (TEGRA_TMRUS_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT) - str r2, [r3, #0xe4] @ pllx_misc - str r1, [r3, #0xe0] @ pllx_base - /* record the time that PLLX will be stable */ + + ldr r1, [r0, #0x0] @ pllx_misc + ldr r2, [r0, #0x4] @ pllx_base + str r1, [r3, #CLK_RESET_PLLX_MISC] + str r2, [r3, #CLK_RESET_PLLX_BASE] + + ldr r1, [r0, #0x8] @ pllp_misc + ldr r2, [r0, #0xc] @ pllp_base + str r1, [r3, #CLK_RESET_PLLP_MISC] + str r2, [r3, #CLK_RESET_PLLP_BASE] + + ldr r1, [r0, #0x10] @ pllp_outa + ldr r2, [r0, #0x14] @ pllp_outb + str r1, [r3, #CLK_RESET_PLLP_OUTA] + str r2, [r3, #CLK_RESET_PLLP_OUTB] + + /* record the time that PLLX and PLLP will be stable */ ldr r1, [r4] add r1, r1, #300 - str r1, [r0, #0x10] + str r1, [r0, #0x18] @ pll_timeout /* FIXME: need to record actual power transition here */ mov r0, #0 b __cortex_a9_l2x0_restart @@ -156,7 +175,7 @@ ENDPROC(tegra_lp2_startup) __tegra_lp2_data: .long . .long tegra_pgd_phys - .long __restart_pllx + .long __restart_plls .size __tegra_lp2_data, . - __tegra_lp2_data #ifdef CONFIG_HOTPLUG_CPU diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c index 06bafa1c45c6..aef50319852c 100644 --- a/arch/arm/mach-tegra/suspend.c +++ b/arch/arm/mach-tegra/suspend.c @@ -53,15 +53,21 @@ #include "board.h" #include "power.h" -/* NOTE: only add elements to the end of this structure, since the assembly - * code uses hard-coded offsets */ -struct suspend_context -{ +struct suspend_context { + /* + * The next 7 values are referenced by offset in __restart_plls + * in headsmp-t2.S, and should not be moved + */ + u32 pllx_misc; + u32 pllx_base; + u32 pllp_misc; + u32 pllp_base; + u32 pllp_outa; + u32 pllp_outb; + u32 pll_timeout; + u32 cpu_burst; u32 clk_csite_src; - u32 pllx_base; - u32 pllx_misc; - u32 pllx_timeout; u32 twd_ctrl; u32 twd_load; u32 cclk_divider; @@ -101,6 +107,11 @@ static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE); #define CLK_RESET_PLLM_BASE 0x90 #define CLK_RESET_PLLX_BASE 0xe0 #define CLK_RESET_PLLX_MISC 0xe4 +#define CLK_RESET_PLLP_BASE 0xa0 +#define CLK_RESET_PLLP_OUTA 0xa4 +#define CLK_RESET_PLLP_OUTB 0xa8 +#define CLK_RESET_PLLP_MISC 0xac + #define CLK_RESET_SOURCE_CSITE 0x1d4 @@ -256,7 +267,7 @@ static noinline void restore_cpu_complex(void) BUG_ON(readl(clk_rst + CLK_RESET_PLLX_BASE) != tegra_sctx.pllx_base); if (tegra_sctx.pllx_base & (1<<30)) { - while (readl(tmrus)-tegra_sctx.pllx_timeout >= 0x80000000UL) + while (readl(tmrus)-tegra_sctx.pll_timeout >= 0x80000000UL) cpu_relax(); } writel(tegra_sctx.cclk_divider, clk_rst + CLK_RESET_CCLK_DIVIDER); @@ -295,6 +306,10 @@ static noinline void suspend_cpu_complex(void) tegra_sctx.cpu_burst = readl(clk_rst + CLK_RESET_CCLK_BURST); tegra_sctx.pllx_base = readl(clk_rst + CLK_RESET_PLLX_BASE); tegra_sctx.pllx_misc = readl(clk_rst + CLK_RESET_PLLX_MISC); + tegra_sctx.pllp_base = readl(clk_rst + CLK_RESET_PLLP_BASE); + tegra_sctx.pllp_outa = readl(clk_rst + CLK_RESET_PLLP_OUTA); + tegra_sctx.pllp_outb = readl(clk_rst + CLK_RESET_PLLP_OUTB); + tegra_sctx.pllp_misc = readl(clk_rst + CLK_RESET_PLLP_MISC); tegra_sctx.cclk_divider = readl(clk_rst + CLK_RESET_CCLK_DIVIDER); #ifdef CONFIG_HAVE_ARM_TWD From 72c7a306e961fa47ae7f6da152703cf439e99b0a Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 26 Oct 2010 17:33:31 -0700 Subject: [PATCH 4/8] ARM: tegra: clock: Don't restore PLLP registers The PLLP registers are now being restored by the low-level resume code, and the CPU may be running off PLLP, so don't touch them during clock resume. Change-Id: Ida248a929c8b59f2e51f43dbbef1cd792ef68737 Signed-off-by: Colin Cross --- arch/arm/mach-tegra/tegra2_clocks.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index 4a9e3e0b7f2e..253de093a8ef 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -2053,7 +2053,7 @@ void __init tegra2_init_clocks(void) #ifdef CONFIG_PM static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM + - PERIPH_CLK_SOURCE_NUM + 19]; + PERIPH_CLK_SOURCE_NUM + 15]; void tegra_clk_suspend(void) { @@ -2061,16 +2061,12 @@ void tegra_clk_suspend(void) u32 *ctx = clk_rst_suspend; *ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK; - *ctx++ = clk_readl(tegra_pll_p.reg + PLL_BASE); - *ctx++ = clk_readl(tegra_pll_p.reg + PLL_MISC(&tegra_pll_p)); *ctx++ = clk_readl(tegra_pll_c.reg + PLL_BASE); *ctx++ = clk_readl(tegra_pll_c.reg + PLL_MISC(&tegra_pll_c)); *ctx++ = clk_readl(tegra_pll_a.reg + PLL_BASE); *ctx++ = clk_readl(tegra_pll_a.reg + PLL_MISC(&tegra_pll_a)); *ctx++ = clk_readl(tegra_pll_m_out1.reg); - *ctx++ = clk_readl(tegra_pll_p_out1.reg); - *ctx++ = clk_readl(tegra_pll_p_out3.reg); *ctx++ = clk_readl(tegra_pll_a_out0.reg); *ctx++ = clk_readl(tegra_pll_c_out1.reg); @@ -2110,8 +2106,6 @@ void tegra_clk_resume(void) val |= *ctx++; clk_writel(val, OSC_CTRL); - clk_writel(*ctx++, tegra_pll_p.reg + PLL_BASE); - clk_writel(*ctx++, tegra_pll_p.reg + PLL_MISC(&tegra_pll_p)); clk_writel(*ctx++, tegra_pll_c.reg + PLL_BASE); clk_writel(*ctx++, tegra_pll_c.reg + PLL_MISC(&tegra_pll_c)); clk_writel(*ctx++, tegra_pll_a.reg + PLL_BASE); @@ -2119,8 +2113,6 @@ void tegra_clk_resume(void) udelay(300); clk_writel(*ctx++, tegra_pll_m_out1.reg); - clk_writel(*ctx++, tegra_pll_p_out1.reg); - clk_writel(*ctx++, tegra_pll_p_out3.reg); clk_writel(*ctx++, tegra_pll_a_out0.reg); clk_writel(*ctx++, tegra_pll_c_out1.reg); From 185ddab215a2d9cb06d49e7a42ce8abd5925d2fd Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Tue, 26 Oct 2010 14:25:48 -0700 Subject: [PATCH 5/8] video: tegra: add IOCTL for querying modes Change-Id: Ic312271b96a8bb8d8b2696448d3ff7fd73a7a851 Signed-off-by: Erik Gilling --- drivers/video/tegra/fb.c | 26 ++++++++++++++++++++++++++ include/video/tegrafb.h | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index ef4e4c3e3708..a1045957bdae 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -499,6 +499,9 @@ static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long { struct tegra_fb_info *tegra_fb = info->par; struct tegra_fb_flip_args flip_args; + struct tegra_fb_modedb modedb; + struct fb_modelist *modelist; + int i; int fd; int ret; @@ -520,6 +523,29 @@ static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long return ret; + case FBIO_TEGRA_GET_MODEDB: + if (copy_from_user(&modedb, (void __user *)arg, sizeof(modedb))) + return -EFAULT; + + i = 0; + list_for_each_entry(modelist, &info->modelist, list) { + struct fb_var_screeninfo var; + + if (i >= modedb.modedb_len) + break; + fb_videomode_to_var(&var, &modelist->mode); + + if (copy_to_user((void __user *)&modedb.modedb[i], + &var, sizeof(var))) + return -EFAULT; + i++; + } + modedb.modedb_len = i; + + if (copy_to_user((void __user *)arg, &modedb, sizeof(modedb))) + return -EFAULT; + break; + default: return -ENOTTY; } diff --git a/include/video/tegrafb.h b/include/video/tegrafb.h index 3d7a5a9d66e9..79578c3d7bd4 100644 --- a/include/video/tegrafb.h +++ b/include/video/tegrafb.h @@ -18,6 +18,7 @@ #ifndef _LINUX_TEGRAFB_H_ #define _LINUX_TEGRAFB_H_ +#include #include #include @@ -77,7 +78,13 @@ struct tegra_fb_flip_args { __u32 post_syncpt_val; }; +struct tegra_fb_modedb { + struct fb_var_screeninfo *modedb; + __u32 modedb_len; +}; + #define FBIO_TEGRA_SET_NVMAP_FD _IOW('F', 0x40, __u32) #define FBIO_TEGRA_FLIP _IOW('F', 0x41, struct tegra_fb_flip_args) +#define FBIO_TEGRA_GET_MODEDB _IOWR('F', 0x42, struct tegra_fb_modedb) #endif From 06d191b17b944de22e1fd36fad24d32107e283b2 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Tue, 26 Oct 2010 15:21:23 -0700 Subject: [PATCH 6/8] video: tegra: allow setting output mode without changing fb geometry Change-Id: I0a723b4e68c99d2727e12d2c26f1506b1f634f8a Signed-off-by: Erik Gilling --- drivers/video/tegra/fb.c | 55 ++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/video/tegra/fb.c b/drivers/video/tegra/fb.c index a1045957bdae..1d518a561242 100644 --- a/drivers/video/tegra/fb.c +++ b/drivers/video/tegra/fb.c @@ -120,36 +120,35 @@ static int tegra_fb_set_par(struct fb_info *info) struct tegra_fb_info *tegra_fb = info->par; struct fb_var_screeninfo *var = &info->var; - /* we only support RGB ordering for now */ - switch (var->bits_per_pixel) { - case 32: - case 24: - var->red.offset = 0; - var->red.length = 8; - var->green.offset = 8; - var->green.length = 8; - var->blue.offset = 16; - var->blue.length = 8; - tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8; - break; - case 16: - var->red.offset = 11; - var->red.length = 5; - var->green.offset = 5; - var->green.length = 6; - var->blue.offset = 0; - var->blue.length = 5; - tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5; - break; + if (var->bits_per_pixel) { + /* we only support RGB ordering for now */ + switch (var->bits_per_pixel) { + case 32: + case 24: + var->red.offset = 0; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 16; + var->blue.length = 8; + tegra_fb->win->fmt = TEGRA_WIN_FMT_R8G8B8A8; + break; + case 16: + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + tegra_fb->win->fmt = TEGRA_WIN_FMT_B5G6R5; + break; - case 0: - break; - - default: - return -EINVAL; + default: + return -EINVAL; + } + info->fix.line_length = var->xres * var->bits_per_pixel / 8; + tegra_fb->win->stride = info->fix.line_length; } - info->fix.line_length = var->xres * var->bits_per_pixel / 8; - tegra_fb->win->stride = info->fix.line_length; if (var->pixclock) { struct tegra_dc_mode mode; From e4886b1e1b098af06720724770622caaa9721dc9 Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Tue, 26 Oct 2010 15:29:11 -0700 Subject: [PATCH 7/8] video: tegra: transmute HDMI modes into supported ones Change-Id: I37222e9d1617a0ace062e05740365cfe5c0c2af8 Signed-off-by: Erik Gilling --- drivers/video/tegra/dc/hdmi.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c index 58ad135b90cc..acc3b36ea046 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -391,23 +391,26 @@ static void hdmi_dumpregs(struct tegra_dc_hdmi_data *hdmi) static bool tegra_dc_hdmi_mode_equal(const struct fb_videomode *mode1, const struct fb_videomode *mode2) { - int diff = (s64)mode1->pixclock - (s64)mode2->pixclock; - return mode1->xres == mode2->xres && mode1->yres == mode2->yres && - diff < PIXCLOCK_TOLERANCE && - diff > -PIXCLOCK_TOLERANCE && mode1->vmode == mode2->vmode; } static bool tegra_dc_hdmi_mode_filter(struct fb_videomode *mode) { int i; + int clocks; for (i = 0; i < ARRAY_SIZE(tegra_dc_hdmi_supported_modes); i++) { if (tegra_dc_hdmi_mode_equal(&tegra_dc_hdmi_supported_modes[i], - mode)) + mode)) { + memcpy(mode, &tegra_dc_hdmi_supported_modes[i], sizeof(*mode)); + mode->flag = FB_MODE_IS_DETAILED; + clocks = (mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len) * + (mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len); + mode->refresh = (PICOS2KHZ(mode->pixclock) * 1000) / clocks; return true; + } } return false; From 157e610c36957f0887ec0974e3ab2242e91894ee Mon Sep 17 00:00:00 2001 From: Erik Gilling Date: Tue, 26 Oct 2010 15:30:35 -0700 Subject: [PATCH 8/8] video: tegra: flesh out edid support * read blocks in a single command instead of byte at a time * allow reading past segment 0 (edid > 256 bytes) * handle mutiple extention blocks * add debugfs file for reading edid Change-Id: Iec8182cdbccdaa2142e4bbc892202d2e8d73c23b Signed-off-by: Erik Gilling --- drivers/video/tegra/dc/edid.c | 195 +++++++++++++++++++++++++++++++--- drivers/video/tegra/dc/edid.h | 8 +- 2 files changed, 182 insertions(+), 21 deletions(-) diff --git a/drivers/video/tegra/dc/edid.c b/drivers/video/tegra/dc/edid.c index 0b0b1a760a4e..812a0087a96d 100644 --- a/drivers/video/tegra/dc/edid.c +++ b/drivers/video/tegra/dc/edid.c @@ -15,31 +15,181 @@ * */ -#include +#define DEBUG + +#include #include +#include +#include +#include #include "edid.h" +struct tegra_edid { + struct i2c_client *client; + struct i2c_board_info info; + int bus; + + u8 *data; + unsigned len; +}; + +#if defined(DEBUG) || defined(CONFIG_DEBUG_FS) +static int tegra_edid_show(struct seq_file *s, void *unused) +{ + struct tegra_edid *edid = s->private; + int i; + + for (i = 0; i < edid->len; i++) { + if (i % 16 == 0) + seq_printf(s, "edid[%03x] =", i); + + seq_printf(s, " %02x", edid->data[i]); + + if (i % 16 == 15) + seq_printf(s, "\n"); + } + + return 0; +} +#endif + +#ifdef CONFIG_DEBUG_FS +static int tegra_edid_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tegra_edid_show, inode->i_private); +} + +static const struct file_operations tegra_edid_debug_fops = { + .open = tegra_edid_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void tegra_edid_debug_add(struct tegra_edid *edid) +{ + char name[] = "edidX"; + + snprintf(name, sizeof(name), "edid%1d", edid->bus); + debugfs_create_file(name, S_IRUGO, NULL, edid, &tegra_edid_debug_fops); +} +#else +void tegra_edid_debug_add(struct tegra_edid *edid) +{ +} +#endif + +#ifdef DEBUG +static char tegra_edid_dump_buff[16 * 1024]; + +static void tegra_edid_dump(struct tegra_edid *edid) +{ + struct seq_file s; + int i; + char c; + + memset(&s, 0x0, sizeof(s)); + + s.buf = tegra_edid_dump_buff; + s.size = sizeof(tegra_edid_dump_buff); + s.private = edid; + + tegra_edid_show(&s, NULL); + + i = 0; + while (i < s.count ) { + if ((s.count - i) > 256) { + c = s.buf[i + 256]; + s.buf[i + 256] = 0; + printk("%s", s.buf + i); + s.buf[i + 256] = c; + } else { + printk("%s", s.buf + i); + } + i += 256; + } +} +#else +static void tegra_edid_dump(struct tegra_edid *edid) +{ +} +#endif + +int tegra_edid_read_block(struct tegra_edid *edid, int block, u8 *data) +{ + u8 block_buf[] = {block >> 1}; + u8 cmd_buf[] = {(block & 0x1) * 128}; + int status; + struct i2c_msg msg[] = { + { + .addr = 0x30, + .flags = 0, + .len = 1, + .buf = block_buf, + }, + { + .addr = 0x50, + .flags = 0, + .len = 1, + .buf = cmd_buf, + }, + { + .addr = 0x50, + .flags = I2C_M_RD, + .len = 128, + .buf = data, + }}; + struct i2c_msg *m; + int msg_len; + + if (block > 1) { + msg_len = 3; + m = msg; + } else { + msg_len = 2; + m = &msg[1]; + } + + status = i2c_transfer(edid->client->adapter, m, msg_len); + + if (status < 0) + return status; + + if (status != msg_len) + return -EIO; + + return 0; +} + + int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs) { - u8 data[256]; int i; int ret; + int extension_blocks; - for (i = 0; i < 256; i++) { - ret = i2c_smbus_read_byte_data(edid->client, i); + ret = tegra_edid_read_block(edid, 0, edid->data); + + memset(specs, 0x0, sizeof(struct fb_monspecs)); + fb_edid_to_monspecs(edid->data, specs); + if (specs->modedb == NULL) + return -EINVAL; + + extension_blocks = edid->data[0x7e]; + + for (i = 1; i <= extension_blocks; i++) { + ret = tegra_edid_read_block(edid, i, edid->data + i * 128); if (ret < 0) break; - data[i] = ret; + if (edid->data[i * 128] == 0x2) + fb_edid_add_monspecs(edid->data + i * 128, specs); } - if (i != 128 && i != 256) - return ret; + edid->len = i * 128; - fb_edid_to_monspecs(data, specs); - if (i == 256) - fb_edid_add_monspecs(data + 128, specs); + tegra_edid_dump(edid); return 0; } @@ -48,36 +198,53 @@ struct tegra_edid *tegra_edid_create(int bus) { struct tegra_edid *edid; struct i2c_adapter *adapter; + int err; edid = kzalloc(sizeof(struct tegra_edid), GFP_KERNEL); if (!edid) return ERR_PTR(-ENOMEM); + edid->data = vmalloc(SZ_32K); + if (!edid->data) { + err = -ENOMEM; + goto free_edid; + } strlcpy(edid->info.type, "tegra_edid", sizeof(edid->info.type)); + edid->bus = bus; edid->info.addr = 0x50; edid->info.platform_data = edid; - init_waitqueue_head(&edid->wq); adapter = i2c_get_adapter(bus); if (!adapter) { pr_err("can't get adpater for bus %d\n", bus); - return NULL; + err = -EBUSY; + goto free_edid; } - edid->client = i2c_new_device(adapter, &edid->info); + edid->client = i2c_new_device(adapter, &edid->info); i2c_put_adapter(adapter); if (!edid->client) { pr_err("can't create new device\n"); - return NULL; + err = -EBUSY; + goto free_edid; } + tegra_edid_debug_add(edid); + return edid; + +free_edid: + vfree(edid->data); + kfree(edid); + + return ERR_PTR(err); } void tegra_edid_destroy(struct tegra_edid *edid) { i2c_release_client(edid->client); + vfree(edid->data); kfree(edid); } diff --git a/drivers/video/tegra/dc/edid.h b/drivers/video/tegra/dc/edid.h index eb78abb7e7d8..821da90a8b4f 100644 --- a/drivers/video/tegra/dc/edid.h +++ b/drivers/video/tegra/dc/edid.h @@ -21,13 +21,7 @@ #include #include -struct tegra_edid { - struct i2c_client *client; - struct i2c_board_info info; - - int probed; - wait_queue_head_t wq; -}; +struct tegra_edid; struct tegra_edid *tegra_edid_create(int bus); void tegra_edid_destroy(struct tegra_edid *edid);