1115 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1115 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* drivers/video/msm_fb/mdp.c
 | 
						|
 *
 | 
						|
 * MSM MDP Interface (used by framebuffer core)
 | 
						|
 *
 | 
						|
 * Copyright (C) 2007 QUALCOMM Incorporated
 | 
						|
 * Copyright (C) 2007 Google Incorporated
 | 
						|
 *
 | 
						|
 * This software is licensed under the terms of the GNU General Public
 | 
						|
 * License version 2, as published by the Free Software Foundation, and
 | 
						|
 * may be copied, distributed, and modified under those terms.
 | 
						|
 *
 | 
						|
 * 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/kernel.h>
 | 
						|
#include <linux/fb.h>
 | 
						|
#include <linux/msm_mdp.h>
 | 
						|
#include <linux/interrupt.h>
 | 
						|
#include <linux/wait.h>
 | 
						|
#include <linux/clk.h>
 | 
						|
#include <linux/file.h>
 | 
						|
#include <linux/android_pmem.h>
 | 
						|
#include <linux/major.h>
 | 
						|
#include <linux/msm_hw3d.h>
 | 
						|
 | 
						|
#include <mach/msm_iomap.h>
 | 
						|
#include <mach/msm_fb.h>
 | 
						|
#include <linux/platform_device.h>
 | 
						|
 | 
						|
#include "mdp_hw.h"
 | 
						|
#include "mdp_ppp.h"
 | 
						|
#include <asm/mach-types.h>
 | 
						|
 | 
						|
struct class *mdp_class;
 | 
						|
 | 
						|
#define MDP_CMD_DEBUG_ACCESS_BASE (0x10000)
 | 
						|
 | 
						|
static DECLARE_WAIT_QUEUE_HEAD(mdp_ppp_waitqueue);
 | 
						|
static unsigned int mdp_irq_mask;
 | 
						|
struct clk *mdp_clk_to_disable_later = 0;
 | 
						|
static struct  mdp_blit_req *timeout_req;
 | 
						|
#ifdef CONFIG_FB_MSM_OVERLAY
 | 
						|
extern int mdp4_overlay_get(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req);
 | 
						|
extern int mdp4_overlay_set(struct mdp_device *mdp_dev, struct fb_info *info, struct mdp_overlay *req);
 | 
						|
extern int mdp4_overlay_unset(struct mdp_device *mdp_dev, struct fb_info *info, int ndx);
 | 
						|
extern int mdp4_overlay_play(struct mdp_device *mdp_dev, struct fb_info *info, struct msmfb_overlay_data *req,
 | 
						|
				struct file **pp_src_file);
 | 
						|
extern void mdp4_mddi_overlay(void *priv, uint32_t addr, uint32_t stride,
 | 
						|
			    uint32_t width, uint32_t height, uint32_t x,
 | 
						|
			    uint32_t y);
 | 
						|
#include "mdp4.h"
 | 
						|
#endif
 | 
						|
DEFINE_MUTEX(mdp_mutex);
 | 
						|
 | 
						|
static int locked_enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
 | 
						|
{
 | 
						|
	BUG_ON(!mask);
 | 
						|
 | 
						|
	/* if the mask bits are already set return an error, this interrupt
 | 
						|
	 * is already enabled */
 | 
						|
	if (mdp_irq_mask & mask) {
 | 
						|
		pr_err("mdp irq already on %x %x\n", mdp_irq_mask, mask);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	/* if the mdp irq is not already enabled enable it */
 | 
						|
	if (!mdp_irq_mask) {
 | 
						|
		clk_enable(mdp->clk);
 | 
						|
		enable_irq(mdp->irq);
 | 
						|
		clk_set_rate(mdp->ebi1_clk, 128000000);
 | 
						|
	}
 | 
						|
 | 
						|
	/* clear out any previous irqs for the requested mask*/
 | 
						|
	mdp_writel(mdp, mask, MDP_INTR_CLEAR);
 | 
						|
 | 
						|
	/* update the irq mask to reflect the fact that the interrupt is
 | 
						|
	 * enabled */
 | 
						|
	mdp_irq_mask |= mask;
 | 
						|
	mdp_writel(mdp, mdp_irq_mask, MDP_INTR_ENABLE);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int enable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
 | 
						|
{
 | 
						|
	unsigned long flags;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	spin_lock_irqsave(&mdp->lock, flags);
 | 
						|
	ret = locked_enable_mdp_irq(mdp, mask);
 | 
						|
	spin_unlock_irqrestore(&mdp->lock, flags);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int locked_disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
 | 
						|
{
 | 
						|
	/* this interrupt is already disabled! */
 | 
						|
	if (!(mdp_irq_mask & mask)) {
 | 
						|
		printk(KERN_ERR "mdp irq already off %x %x\n",
 | 
						|
		       mdp_irq_mask, mask);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
	/* update the irq mask to reflect the fact that the interrupt is
 | 
						|
	 * disabled */
 | 
						|
	mdp_irq_mask &= ~(mask);
 | 
						|
	mdp_writel(mdp, mdp_irq_mask, MDP_INTR_ENABLE);
 | 
						|
 | 
						|
	/* if no one is waiting on the interrupt, disable it */
 | 
						|
	if (!mdp_irq_mask) {
 | 
						|
		disable_irq_nosync(mdp->irq);
 | 
						|
		if (mdp->clk)
 | 
						|
			clk_disable(mdp->clk);
 | 
						|
		clk_set_rate(mdp->ebi1_clk, 0);
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int disable_mdp_irq(struct mdp_info *mdp, uint32_t mask)
 | 
						|
{
 | 
						|
	unsigned long irq_flags;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	spin_lock_irqsave(&mdp->lock, irq_flags);
 | 
						|
	ret = locked_disable_mdp_irq(mdp, mask);
 | 
						|
	spin_unlock_irqrestore(&mdp->lock, irq_flags);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static irqreturn_t mdp_isr(int irq, void *data)
 | 
						|
{
 | 
						|
	uint32_t status;
 | 
						|
	unsigned long irq_flags;
 | 
						|
	struct mdp_info *mdp = data;
 | 
						|
	int i;
 | 
						|
 | 
						|
	spin_lock_irqsave(&mdp->lock, irq_flags);
 | 
						|
 | 
						|
	status = mdp_readl(mdp, MDP_INTR_STATUS);
 | 
						|
	mdp_writel(mdp, status, MDP_INTR_CLEAR);
 | 
						|
 | 
						|
 | 
						|
#if defined(CONFIG_MACH_HTCLEO)
 | 
						|
	status &= ~0x10000; // Cotulla
 | 
						|
#endif
 | 
						|
 | 
						|
//	pr_info("%s: status=%08x (irq_mask=%08x)\n", __func__, status,
 | 
						|
//		mdp_irq_mask);
 | 
						|
	status &= mdp_irq_mask;
 | 
						|
#ifdef CONFIG_MSM_MDP40
 | 
						|
	if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) {
 | 
						|
		if(status && mdp->out_if[MSM_MDDI_PMDH_INTERFACE].dma_cb != NULL)
 | 
						|
			status |= (INTR_OVERLAY0_DONE | MDP_DMA_S_DONE);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	for (i = 0; i < MSM_MDP_NUM_INTERFACES; ++i) {
 | 
						|
		struct mdp_out_interface *out_if = &mdp->out_if[i];
 | 
						|
		if (status & out_if->dma_mask) {
 | 
						|
			if (out_if->dma_cb) {
 | 
						|
				out_if->dma_cb->func(out_if->dma_cb);
 | 
						|
				out_if->dma_cb = NULL;
 | 
						|
			}
 | 
						|
			wake_up(&out_if->dma_waitqueue);
 | 
						|
		}
 | 
						|
		if (status & out_if->irq_mask) {
 | 
						|
			out_if->irq_cb->func(out_if->irq_cb);
 | 
						|
			out_if->irq_cb = NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (status & DL0_ROI_DONE)
 | 
						|
		wake_up(&mdp_ppp_waitqueue);
 | 
						|
 | 
						|
	if (status)
 | 
						|
		locked_disable_mdp_irq(mdp, status);
 | 
						|
 | 
						|
	spin_unlock_irqrestore(&mdp->lock, irq_flags);
 | 
						|
	return IRQ_HANDLED;
 | 
						|
}
 | 
						|
 | 
						|
static uint32_t mdp_check_mask(struct mdp_info *mdp, uint32_t mask)
 | 
						|
{
 | 
						|
	uint32_t ret;
 | 
						|
	unsigned long irq_flags;
 | 
						|
 | 
						|
	spin_lock_irqsave(&mdp->lock, irq_flags);
 | 
						|
	ret = mdp_irq_mask & mask;
 | 
						|
	spin_unlock_irqrestore(&mdp->lock, irq_flags);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int mdp_wait(struct mdp_info *mdp, uint32_t mask, wait_queue_head_t *wq)
 | 
						|
{
 | 
						|
	int ret = 0;
 | 
						|
	unsigned long irq_flags;
 | 
						|
 | 
						|
//	pr_info("%s: WAITING for 0x%x\n", __func__, mask);
 | 
						|
	wait_event_timeout(*wq, !mdp_check_mask(mdp, mask), HZ);
 | 
						|
 | 
						|
	spin_lock_irqsave(&mdp->lock, irq_flags);
 | 
						|
	if (mdp_irq_mask & mask) {
 | 
						|
		locked_disable_mdp_irq(mdp, mask);
 | 
						|
		pr_warning("%s: timeout waiting for mdp to complete 0x%x\n",
 | 
						|
			   __func__, mask);
 | 
						|
	if(timeout_req)
 | 
						|
		mdp_dump_blit(timeout_req);
 | 
						|
 | 
						|
		ret = -ETIMEDOUT;
 | 
						|
	} else {
 | 
						|
//		pr_info("%s: SUCCESS waiting for 0x%x\n", __func__, mask);
 | 
						|
	}
 | 
						|
	spin_unlock_irqrestore(&mdp->lock, irq_flags);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void mdp_dma_wait(struct mdp_device *mdp_dev, int interface)
 | 
						|
{
 | 
						|
#define MDP_MAX_TIMEOUTS 20
 | 
						|
	static int timeout_count;
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
	unsigned int mask = 0;
 | 
						|
	wait_queue_head_t *wq;
 | 
						|
 | 
						|
	switch (interface) {
 | 
						|
	case MSM_MDDI_PMDH_INTERFACE:
 | 
						|
	case MSM_MDDI_EMDH_INTERFACE:
 | 
						|
	case MSM_LCDC_INTERFACE:
 | 
						|
	case MSM_TV_INTERFACE:
 | 
						|
		BUG_ON(!mdp->out_if[interface].registered);
 | 
						|
		mask = mdp->out_if[interface].dma_mask;
 | 
						|
		wq = &mdp->out_if[interface].dma_waitqueue;
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		pr_err("%s: Unknown interface %d\n", __func__, interface);
 | 
						|
		BUG();
 | 
						|
	}
 | 
						|
 | 
						|
	if (mdp_wait(mdp, mask, wq) == -ETIMEDOUT)
 | 
						|
		timeout_count++;
 | 
						|
	else
 | 
						|
		timeout_count = 0;
 | 
						|
 | 
						|
	if (timeout_count > MDP_MAX_TIMEOUTS) {
 | 
						|
		printk(KERN_ERR "mdp: dma failed %d times, somethings wrong!\n",
 | 
						|
		       MDP_MAX_TIMEOUTS);
 | 
						|
		BUG();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int mdp_ppp_wait(struct mdp_info *mdp)
 | 
						|
{
 | 
						|
	return mdp_wait(mdp, DL0_ROI_DONE, &mdp_ppp_waitqueue);
 | 
						|
}
 | 
						|
 | 
						|
static void mdp_dmas_to_mddi(void *priv, uint32_t addr, uint32_t stride,
 | 
						|
		uint32_t width, uint32_t height, uint32_t x, uint32_t y)
 | 
						|
{
 | 
						|
	struct mdp_info *mdp = priv;
 | 
						|
	uint32_t dma2_cfg;
 | 
						|
	uint32_t video_packet_parameter;
 | 
						|
	uint16_t ld_param = 1;
 | 
						|
 | 
						|
 | 
						|
	if(machine_is_htcleo()) {
 | 
						|
		dma2_cfg = DMA_PACK_ALIGN_MSB |
 | 
						|
			DMA_PACK_PATTERN_RGB;
 | 
						|
 | 
						|
		dma2_cfg |= mdp->format;
 | 
						|
 | 
						|
		dma2_cfg |= DMA_OUT_SEL_LCDC;
 | 
						|
 | 
						|
		dma2_cfg |= DMA_IBUF_FORMAT_RGB565;
 | 
						|
		
 | 
						|
	} else {
 | 
						|
		dma2_cfg = DMA_PACK_TIGHT |
 | 
						|
			DMA_PACK_ALIGN_LSB |
 | 
						|
			DMA_OUT_SEL_AHB |
 | 
						|
			DMA_IBUF_NONCONTIGUOUS;
 | 
						|
 | 
						|
		dma2_cfg |= mdp->format;
 | 
						|
 | 
						|
	#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
 | 
						|
		if (mdp->format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888)
 | 
						|
	#else
 | 
						|
		if (mdp->format == DMA_IBUF_FORMAT_XRGB8888)
 | 
						|
	#endif
 | 
						|
			dma2_cfg |= DMA_PACK_PATTERN_BGR;
 | 
						|
		else
 | 
						|
			dma2_cfg |= DMA_PACK_PATTERN_RGB;
 | 
						|
 | 
						|
			dma2_cfg |= DMA_OUT_SEL_MDDI;
 | 
						|
 | 
						|
			dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
 | 
						|
 | 
						|
			dma2_cfg |= DMA_DITHER_EN;
 | 
						|
	}
 | 
						|
 | 
						|
	if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) {
 | 
						|
		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
 | 
						|
		video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565;
 | 
						|
	} else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) {
 | 
						|
		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
 | 
						|
		video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666;
 | 
						|
	}
 | 
						|
 | 
						|
	/* setup size, address, and stride */
 | 
						|
	mdp_writel(mdp, (height << 16) | (width), MDP_DMA_S_SIZE);
 | 
						|
	mdp_writel(mdp, addr, MDP_DMA_S_IBUF_ADDR);
 | 
						|
	mdp_writel(mdp, stride, MDP_DMA_S_IBUF_Y_STRIDE);
 | 
						|
 | 
						|
	/* set y & x offset and MDDI transaction parameters */
 | 
						|
	mdp_writel(mdp, (y << 16) | (x), MDP_DMA_S_OUT_XY);
 | 
						|
	mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
 | 
						|
	if (mdp->mdp_dev.overrides & MSM_MDP_PANEL_IGNORE_PIXEL_DATA) {
 | 
						|
		mdp_writel(mdp, (video_packet_parameter << 16) | 0xE3,
 | 
						|
			MDP_MDDI_PARAM);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
 | 
						|
			MDP_MDDI_PARAM);
 | 
						|
	}
 | 
						|
 | 
						|
	mdp_writel(mdp, dma2_cfg, MDP_DMA_S_CONFIG);
 | 
						|
	mdp_writel(mdp, 0, MDP_DMA_S_START);
 | 
						|
}
 | 
						|
 | 
						|
static void mdp_dma_to_mddi(void *priv, uint32_t addr, uint32_t stride,
 | 
						|
			    uint32_t width, uint32_t height, uint32_t x,
 | 
						|
			    uint32_t y)
 | 
						|
{
 | 
						|
	struct mdp_info *mdp = priv;
 | 
						|
	uint32_t dma2_cfg = 0;
 | 
						|
	uint32_t video_packet_parameter = 0;
 | 
						|
	uint16_t ld_param = 0; /* 0=PRIM, 1=SECD, 2=EXT */
 | 
						|
 | 
						|
#if !defined(CONFIG_MSM_MDP30)
 | 
						|
	dma2_cfg = DMA_PACK_TIGHT |
 | 
						|
		DMA_PACK_ALIGN_LSB |
 | 
						|
		DMA_OUT_SEL_AHB |
 | 
						|
		DMA_IBUF_NONCONTIGUOUS;
 | 
						|
 | 
						|
#endif
 | 
						|
	dma2_cfg |= mdp->format;
 | 
						|
 | 
						|
#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
 | 
						|
	if (mdp->format == DMA_IBUF_FORMAT_RGB888_OR_ARGB8888)
 | 
						|
#else
 | 
						|
	if (mdp->format == DMA_IBUF_FORMAT_XRGB8888)
 | 
						|
#endif
 | 
						|
		dma2_cfg |= DMA_PACK_PATTERN_BGR;
 | 
						|
	else
 | 
						|
		dma2_cfg |= DMA_PACK_PATTERN_RGB;
 | 
						|
 | 
						|
	dma2_cfg |= DMA_OUT_SEL_MDDI;
 | 
						|
 | 
						|
	dma2_cfg |= DMA_MDDI_DMAOUT_LCD_SEL_PRIMARY;
 | 
						|
 | 
						|
#if !defined(CONFIG_MSM_MDP30)
 | 
						|
	dma2_cfg |= DMA_DITHER_EN;
 | 
						|
#endif
 | 
						|
 | 
						|
	if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB565) {
 | 
						|
		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_5BITS | DMA_DSTC2R_5BITS;
 | 
						|
		video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB565;
 | 
						|
	} else if (mdp->mdp_dev.color_format == MSM_MDP_OUT_IF_FMT_RGB666) {
 | 
						|
		dma2_cfg |= DMA_DSTC0G_6BITS | DMA_DSTC1B_6BITS | DMA_DSTC2R_6BITS;
 | 
						|
		video_packet_parameter = MDDI_VDO_PACKET_DESC_RGB666;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
#if defined(CONFIG_MSM_MDP30) || defined(CONFIG_MSM_MDP302)
 | 
						|
	writel(height << 16 | width, mdp->base + 0x90004);
 | 
						|
	writel(addr, mdp->base + 0x90008);
 | 
						|
	writel(stride, mdp->base + 0x9000c);
 | 
						|
 | 
						|
	/* set y & x offset and MDDI transaction parameters */
 | 
						|
	writel(y << 16 | x, mdp->base + 0x90010);
 | 
						|
	writel(ld_param, mdp->base + 0x00090);
 | 
						|
	writel((video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
 | 
						|
		mdp->base + 0x00094);
 | 
						|
 | 
						|
	writel(dma2_cfg, mdp->base + 0x90000);
 | 
						|
 | 
						|
	/* start DMA2 */
 | 
						|
	writel(0, mdp->base + 0x0044);
 | 
						|
#elif defined(CONFIG_MSM_MDP22)
 | 
						|
	/* setup size, address, and stride */
 | 
						|
	mdp_writel(mdp, (height << 16) | (width),
 | 
						|
		   MDP_CMD_DEBUG_ACCESS_BASE + 0x0184);
 | 
						|
	mdp_writel(mdp, addr, MDP_CMD_DEBUG_ACCESS_BASE + 0x0188);
 | 
						|
	mdp_writel(mdp, stride, MDP_CMD_DEBUG_ACCESS_BASE + 0x018C);
 | 
						|
 | 
						|
	/* set y & x offset and MDDI transaction parameters */
 | 
						|
	mdp_writel(mdp, (y << 16) | (x), MDP_CMD_DEBUG_ACCESS_BASE + 0x0194);
 | 
						|
	mdp_writel(mdp, ld_param, MDP_CMD_DEBUG_ACCESS_BASE + 0x01a0);
 | 
						|
	mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
 | 
						|
		   MDP_CMD_DEBUG_ACCESS_BASE + 0x01a4);
 | 
						|
 | 
						|
	mdp_writel(mdp, dma2_cfg, MDP_CMD_DEBUG_ACCESS_BASE + 0x0180);
 | 
						|
 | 
						|
	/* start DMA2 */
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0044);
 | 
						|
#else
 | 
						|
	/* setup size, address, and stride */
 | 
						|
	mdp_writel(mdp, (height << 16) | (width), MDP_DMA_P_SIZE);
 | 
						|
	mdp_writel(mdp, addr, MDP_DMA_P_IBUF_ADDR);
 | 
						|
	mdp_writel(mdp, stride, MDP_DMA_P_IBUF_Y_STRIDE);
 | 
						|
 | 
						|
	/* set y & x offset and MDDI transaction parameters */
 | 
						|
	mdp_writel(mdp, (y << 16) | (x), MDP_DMA_P_OUT_XY);
 | 
						|
	mdp_writel(mdp, ld_param, MDP_MDDI_PARAM_WR_SEL);
 | 
						|
	mdp_writel(mdp, (video_packet_parameter << 16) | MDDI_VDO_PACKET_PRIM,
 | 
						|
		   MDP_MDDI_PARAM);
 | 
						|
 | 
						|
	mdp_writel(mdp, dma2_cfg, MDP_DMA_P_CONFIG);
 | 
						|
	mdp_writel(mdp, 0, MDP_DMA_P_START);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void mdp_dma(struct mdp_device *mdp_dev, uint32_t addr, uint32_t stride,
 | 
						|
	     uint32_t width, uint32_t height, uint32_t x, uint32_t y,
 | 
						|
	     struct msmfb_callback *callback, int interface)
 | 
						|
{
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
	struct mdp_out_interface *out_if;
 | 
						|
	unsigned long flags;
 | 
						|
 | 
						|
	if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES ||
 | 
						|
	    !mdp->out_if[interface].registered) {
 | 
						|
		pr_err("%s: Unknown interface: %d\n", __func__, interface);
 | 
						|
		BUG();
 | 
						|
	}
 | 
						|
	out_if = &mdp->out_if[interface];
 | 
						|
 | 
						|
	spin_lock_irqsave(&mdp->lock, flags);
 | 
						|
	if (locked_enable_mdp_irq(mdp, out_if->dma_mask)) {
 | 
						|
		pr_err("%s: busy\n", __func__);
 | 
						|
		goto done;
 | 
						|
	}
 | 
						|
 | 
						|
	out_if->dma_cb = callback;
 | 
						|
	out_if->dma_start(out_if->priv, addr, stride, width, height, x, y);
 | 
						|
done:
 | 
						|
	spin_unlock_irqrestore(&mdp->lock, flags);
 | 
						|
}
 | 
						|
 | 
						|
static int get_img(struct mdp_img *img, struct fb_info *info,
 | 
						|
		   unsigned long *start, unsigned long *len,
 | 
						|
		   struct file** filep)
 | 
						|
{
 | 
						|
	int put_needed, ret = 0;
 | 
						|
	struct file *file;
 | 
						|
	unsigned long vstart;
 | 
						|
 | 
						|
	if (!get_pmem_file(img->memory_id, start, &vstart, len, filep))
 | 
						|
		return 0;
 | 
						|
	else if (!get_msm_hw3d_file(img->memory_id, &img->offset, start, len,
 | 
						|
				    filep))
 | 
						|
		return 0;
 | 
						|
 | 
						|
	file = fget_light(img->memory_id, &put_needed);
 | 
						|
	if (file == NULL)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	if (MAJOR(file->f_dentry->d_inode->i_rdev) == FB_MAJOR) {
 | 
						|
		*start = info->fix.smem_start;
 | 
						|
		*len = info->fix.smem_len;
 | 
						|
		ret = 0;
 | 
						|
	} else
 | 
						|
		ret = -1;
 | 
						|
	fput_light(file, put_needed);
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static void put_img(struct file *file)
 | 
						|
{
 | 
						|
	if (file) {
 | 
						|
		if (is_pmem_file(file))
 | 
						|
			put_pmem_file(file);
 | 
						|
		else if (is_msm_hw3d_file(file))
 | 
						|
			put_msm_hw3d_file(file);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void mdp_configure_dma(struct mdp_device *mdp_dev)
 | 
						|
{
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
	uint32_t dma_cfg;
 | 
						|
 | 
						|
	if (!mdp->dma_config_dirty)
 | 
						|
		return;
 | 
						|
	dma_cfg = mdp_readl(mdp, MDP_DMA_P_CONFIG);
 | 
						|
	dma_cfg &= ~DMA_IBUF_FORMAT_MASK;
 | 
						|
	dma_cfg &= ~DMA_PACK_PATTERN_MASK;
 | 
						|
	dma_cfg |= (mdp->format | mdp->pack_pattern);
 | 
						|
	mdp_writel(mdp, dma_cfg, MDP_DMA_P_CONFIG);
 | 
						|
	mdp->dma_config_dirty = false;
 | 
						|
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
int mdp_check_output_format(struct mdp_device *mdp_dev, int bpp)
 | 
						|
{
 | 
						|
	switch (bpp) {
 | 
						|
	case 16:
 | 
						|
	case 24:
 | 
						|
	case 32:
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int mdp_set_output_format(struct mdp_device *mdp_dev, int bpp)
 | 
						|
{
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
	uint32_t format, pack_pattern = DMA_PACK_PATTERN_RGB;
 | 
						|
 | 
						|
	switch (bpp) {
 | 
						|
	case 16:
 | 
						|
		format = DMA_IBUF_FORMAT_RGB565;
 | 
						|
		pack_pattern = DMA_PACK_PATTERN_RGB;
 | 
						|
		break;
 | 
						|
#if defined CONFIG_MSM_MDP22 || defined CONFIG_MSM_MDP30
 | 
						|
	case 24:
 | 
						|
	case 32:
 | 
						|
		format = DMA_IBUF_FORMAT_RGB888_OR_ARGB8888;
 | 
						|
		break;
 | 
						|
#else
 | 
						|
	case 24:
 | 
						|
		format = DMA_IBUF_FORMAT_RGB888;
 | 
						|
		pack_pattern = DMA_PACK_PATTERN_BGR;
 | 
						|
		break;
 | 
						|
	case 32:
 | 
						|
		format = DMA_IBUF_FORMAT_XRGB8888;
 | 
						|
		pack_pattern = DMA_PACK_PATTERN_BGR;
 | 
						|
		break;
 | 
						|
#endif
 | 
						|
	default:
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
	if (format != mdp->format || pack_pattern != mdp->pack_pattern) {
 | 
						|
		mdp->format = format;
 | 
						|
		mdp->pack_pattern = pack_pattern;
 | 
						|
		mdp->dma_config_dirty = true;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void dump_req(struct mdp_blit_req *req,
 | 
						|
	unsigned long src_start, unsigned long src_len,
 | 
						|
	unsigned long dst_start, unsigned long dst_len)
 | 
						|
{
 | 
						|
	pr_err("flags: 0x%x\n", 	req->flags);
 | 
						|
	pr_err("src_start:  0x%08lx\n", src_start);
 | 
						|
	pr_err("src_len:    0x%08lx\n", src_len);
 | 
						|
	pr_err("src.offset: 0x%x\n",    req->src.offset);
 | 
						|
	pr_err("src.format: 0x%x\n",    req->src.format);
 | 
						|
	pr_err("src.width:  %d\n",      req->src.width);
 | 
						|
	pr_err("src.height: %d\n",      req->src.height);
 | 
						|
	pr_err("src_rect.x: %d\n",      req->src_rect.x);
 | 
						|
	pr_err("src_rect.y: %d\n",      req->src_rect.y);
 | 
						|
	pr_err("src_rect.w: %d\n",      req->src_rect.w);
 | 
						|
	pr_err("src_rect.h: %d\n",      req->src_rect.h);
 | 
						|
 | 
						|
	pr_err("dst_start:  0x%08lx\n", dst_start);
 | 
						|
	pr_err("dst_len:    0x%08lx\n", dst_len);
 | 
						|
	pr_err("dst.offset: 0x%x\n",    req->dst.offset);
 | 
						|
	pr_err("dst.format: 0x%x\n",    req->dst.format);
 | 
						|
	pr_err("dst.width:  %d\n",      req->dst.width);
 | 
						|
	pr_err("dst.height: %d\n",      req->dst.height);
 | 
						|
	pr_err("dst_rect.x: %d\n",      req->dst_rect.x);
 | 
						|
	pr_err("dst_rect.y: %d\n",      req->dst_rect.y);
 | 
						|
	pr_err("dst_rect.w: %d\n",      req->dst_rect.w);
 | 
						|
	pr_err("dst_rect.h: %d\n",      req->dst_rect.h);
 | 
						|
}
 | 
						|
 | 
						|
int mdp_blit_and_wait(struct mdp_info *mdp, struct mdp_blit_req *req,
 | 
						|
		struct file *src_file, unsigned long src_start, unsigned long src_len,
 | 
						|
		struct file *dst_file, unsigned long dst_start, unsigned long dst_len)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	enable_mdp_irq(mdp, DL0_ROI_DONE);
 | 
						|
	ret = mdp_ppp_blit(mdp, req,
 | 
						|
			src_file, src_start, src_len,
 | 
						|
			dst_file, dst_start, dst_len);
 | 
						|
	if (unlikely(ret)) {
 | 
						|
		disable_mdp_irq(mdp, DL0_ROI_DONE);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
	ret = mdp_ppp_wait(mdp);
 | 
						|
	if (unlikely(ret)) {
 | 
						|
		printk(KERN_ERR "%s: failed!\n", __func__);
 | 
						|
		pr_err("original request:\n");
 | 
						|
		dump_req(mdp->req, src_start, src_len, dst_start, dst_len);
 | 
						|
		pr_err("dead request:\n");
 | 
						|
		dump_req(req, src_start, src_len, dst_start, dst_len);
 | 
						|
		BUG();
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int mdp_blit(struct mdp_device *mdp_dev, struct fb_info *fb,
 | 
						|
	     struct mdp_blit_req *req)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	unsigned long src_start = 0, src_len = 0, dst_start = 0, dst_len = 0;
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
	struct file *src_file = 0, *dst_file = 0;
 | 
						|
 | 
						|
#if defined(CONFIG_MSM_MDP31) || defined(CONFIG_MSM_MDP302)
 | 
						|
	if (req->flags & MDP_ROT_90) {
 | 
						|
		if (unlikely(((req->dst_rect.h == 1) &&
 | 
						|
			((req->src_rect.w != 1) ||
 | 
						|
			(req->dst_rect.w != req->src_rect.h))) ||
 | 
						|
			((req->dst_rect.w == 1) && ((req->src_rect.h != 1) ||
 | 
						|
			(req->dst_rect.h != req->src_rect.w))))) {
 | 
						|
			pr_err("mpd_ppp: error scaling when size is 1!\n");
 | 
						|
			return -EINVAL;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		if (unlikely(((req->dst_rect.w == 1) &&
 | 
						|
			((req->src_rect.w != 1) ||
 | 
						|
			(req->dst_rect.h != req->src_rect.h))) ||
 | 
						|
			((req->dst_rect.h == 1) && ((req->src_rect.h != 1) ||
 | 
						|
			(req->dst_rect.h != req->src_rect.h))))) {
 | 
						|
			pr_err("mpd_ppp: error scaling when size is 1!\n");
 | 
						|
			return -EINVAL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/* WORKAROUND FOR HARDWARE BUG IN BG TILE FETCH */
 | 
						|
	if (unlikely(req->src_rect.h == 0 ||
 | 
						|
		     req->src_rect.w == 0)) {
 | 
						|
		printk(KERN_ERR "mdp_ppp: src img of zero size!\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
	if (unlikely(req->dst_rect.h == 0 ||
 | 
						|
		     req->dst_rect.w == 0))
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	/* do this first so that if this fails, the caller can always
 | 
						|
	 * safely call put_img */
 | 
						|
	if (unlikely(get_img(&req->src, fb, &src_start, &src_len, &src_file))) {
 | 
						|
		printk(KERN_ERR "mdp_ppp: could not retrieve src image from "
 | 
						|
				"memory\n");
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (unlikely(get_img(&req->dst, fb, &dst_start, &dst_len, &dst_file))) {
 | 
						|
		printk(KERN_ERR "mdp_ppp: could not retrieve dst image from "
 | 
						|
				"memory\n");
 | 
						|
		put_img(src_file);
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
	mutex_lock(&mdp_mutex);
 | 
						|
 | 
						|
	timeout_req = req;
 | 
						|
	/* transp_masking unimplemented */
 | 
						|
	req->transp_mask = MDP_TRANSP_NOP;
 | 
						|
	mdp->req = req;
 | 
						|
#if !defined(CONFIG_MSM_MDP31) && !defined(CONFIG_MSM_MDP302)
 | 
						|
	if (unlikely((req->transp_mask != MDP_TRANSP_NOP ||
 | 
						|
		      req->alpha != MDP_ALPHA_NOP ||
 | 
						|
		      HAS_ALPHA(req->src.format)) &&
 | 
						|
		     (req->flags & MDP_ROT_90 &&
 | 
						|
		      req->dst_rect.w <= 16 && req->dst_rect.h >= 16))) {
 | 
						|
		int i;
 | 
						|
		unsigned int tiles = req->dst_rect.h / 16;
 | 
						|
		unsigned int remainder = req->dst_rect.h % 16;
 | 
						|
		req->src_rect.w = 16*req->src_rect.w / req->dst_rect.h;
 | 
						|
		req->dst_rect.h = 16;
 | 
						|
		for (i = 0; i < tiles; i++) {
 | 
						|
			ret = mdp_blit_and_wait(mdp, req,
 | 
						|
						src_file, src_start, src_len,
 | 
						|
						dst_file, dst_start, dst_len);
 | 
						|
			if (ret)
 | 
						|
				goto end;
 | 
						|
			req->dst_rect.y += 16;
 | 
						|
			req->src_rect.x += req->src_rect.w;
 | 
						|
		}
 | 
						|
		if (!remainder)
 | 
						|
			goto end;
 | 
						|
		req->src_rect.w = remainder*req->src_rect.w / req->dst_rect.h;
 | 
						|
		req->dst_rect.h = remainder;
 | 
						|
	}
 | 
						|
#else
 | 
						|
	/* Workarounds for MDP 3.1 hardware bugs */
 | 
						|
	if (unlikely((mdp_get_bytes_per_pixel(req->dst.format) == 4) &&
 | 
						|
		(req->dst_rect.w != 1) &&
 | 
						|
		(((req->dst_rect.w % 8) == 6) ||
 | 
						|
		((req->dst_rect.w % 32) == 3) ||
 | 
						|
		((req->dst_rect.w % 32) == 1)))) {
 | 
						|
		ret = mdp_ppp_blit_split_width(mdp, req,
 | 
						|
			src_file, src_start, src_len,
 | 
						|
			dst_file, dst_start, dst_len);
 | 
						|
		goto end;
 | 
						|
	} else if (unlikely((req->dst_rect.w != 1) && (req->dst_rect.h != 1) &&
 | 
						|
		((req->dst_rect.h % 32) == 3 ||
 | 
						|
		(req->dst_rect.h % 32) == 1))) {
 | 
						|
		ret = mdp_ppp_blit_split_height(mdp, req,
 | 
						|
			src_file, src_start, src_len,
 | 
						|
			dst_file, dst_start, dst_len);
 | 
						|
		goto end;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	ret = mdp_blit_and_wait(mdp, req,
 | 
						|
				src_file, src_start, src_len,
 | 
						|
				dst_file, dst_start, dst_len);
 | 
						|
end:
 | 
						|
	put_img(src_file);
 | 
						|
	put_img(dst_file);
 | 
						|
	mutex_unlock(&mdp_mutex);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int mdp_fb_mirror(struct mdp_device *mdp_dev,
 | 
						|
		struct fb_info *src_fb, struct fb_info *dst_fb,
 | 
						|
		struct mdp_blit_req *req)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
 | 
						|
	if (!src_fb || !dst_fb)
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	enable_mdp_irq(mdp, DL0_ROI_DONE);
 | 
						|
	ret = mdp_ppp_blit(mdp, req,
 | 
						|
			-1, src_fb->fix.smem_start, src_fb->fix.smem_len,
 | 
						|
			-1, dst_fb->fix.smem_start, dst_fb->fix.smem_len);
 | 
						|
	if (ret)
 | 
						|
		goto err_bad_blit;
 | 
						|
 | 
						|
	ret = mdp_ppp_wait(mdp);
 | 
						|
	if (ret) {
 | 
						|
		pr_err("mdp_ppp_wait error\n");
 | 
						|
		goto err_wait_failed;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
 | 
						|
err_bad_blit:
 | 
						|
	disable_mdp_irq(mdp, DL0_ROI_DONE);
 | 
						|
 | 
						|
err_wait_failed:
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
void mdp_set_grp_disp(struct mdp_device *mdp_dev, unsigned disp_id)
 | 
						|
{
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
 | 
						|
	disp_id &= 0xf;
 | 
						|
	mdp_writel(mdp, disp_id, MDP_FULL_BYPASS_WORD43);
 | 
						|
}
 | 
						|
 | 
						|
/* used by output interface drivers like mddi and lcdc */
 | 
						|
int mdp_out_if_register(struct mdp_device *mdp_dev, int interface,
 | 
						|
			void *private_data, uint32_t dma_mask,
 | 
						|
			mdp_dma_start_func_t dma_start)
 | 
						|
{
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
	unsigned long flags;
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) {
 | 
						|
		pr_err("%s: invalid interface (%d)\n", __func__, interface);
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	spin_lock_irqsave(&mdp->lock, flags);
 | 
						|
 | 
						|
	if (mdp->out_if[interface].registered) {
 | 
						|
		pr_err("%s: interface (%d) already registered\n", __func__,
 | 
						|
		       interface);
 | 
						|
		ret = -EINVAL;
 | 
						|
		goto done;
 | 
						|
	}
 | 
						|
 | 
						|
	init_waitqueue_head(&mdp->out_if[interface].dma_waitqueue);
 | 
						|
	mdp->out_if[interface].registered = 1;
 | 
						|
	mdp->out_if[interface].priv = private_data;
 | 
						|
	mdp->out_if[interface].dma_mask = dma_mask;
 | 
						|
	mdp->out_if[interface].dma_start = dma_start;
 | 
						|
	mdp->out_if[interface].dma_cb = NULL;
 | 
						|
 | 
						|
done:
 | 
						|
	spin_unlock_irqrestore(&mdp->lock, flags);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int mdp_out_if_req_irq(struct mdp_device *mdp_dev, int interface,
 | 
						|
		       uint32_t mask, struct msmfb_callback *cb)
 | 
						|
{
 | 
						|
	struct mdp_info *mdp = container_of(mdp_dev, struct mdp_info, mdp_dev);
 | 
						|
	unsigned long flags;
 | 
						|
	int ret = 0;
 | 
						|
 | 
						|
	if (interface < 0 || interface >= MSM_MDP_NUM_INTERFACES) {
 | 
						|
		pr_err("%s: invalid interface (%d)\n", __func__, interface);
 | 
						|
		BUG();
 | 
						|
	} else if (!mdp->out_if[interface].registered) {
 | 
						|
		pr_err("%s: interface (%d) not registered\n", __func__,
 | 
						|
		       interface);
 | 
						|
		BUG();
 | 
						|
	}
 | 
						|
 | 
						|
	spin_lock_irqsave(&mdp->lock, flags);
 | 
						|
 | 
						|
	if (mask) {
 | 
						|
		ret = locked_enable_mdp_irq(mdp, mask);
 | 
						|
		if (ret) {
 | 
						|
			pr_err("%s: busy\n", __func__);
 | 
						|
			goto done;
 | 
						|
		}
 | 
						|
		mdp->out_if[interface].irq_mask = mask;
 | 
						|
		mdp->out_if[interface].irq_cb = cb;
 | 
						|
	} else {
 | 
						|
		locked_disable_mdp_irq(mdp, mask);
 | 
						|
		mdp->out_if[interface].irq_mask = 0;
 | 
						|
		mdp->out_if[interface].irq_cb = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
done:
 | 
						|
	spin_unlock_irqrestore(&mdp->lock, flags);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int register_mdp_client(struct class_interface *cint)
 | 
						|
{
 | 
						|
	if (!mdp_class) {
 | 
						|
		pr_err("mdp: no mdp_class when registering mdp client\n");
 | 
						|
		return -ENODEV;
 | 
						|
	}
 | 
						|
	cint->class = mdp_class;
 | 
						|
	return class_interface_register(cint);
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_MSM_MDP40
 | 
						|
void mdp_hw_init(struct mdp_info *mdp)
 | 
						|
{
 | 
						|
	mdp_irq_mask = 0;
 | 
						|
	mdp_writel(mdp, 0, MDP_INTR_ENABLE);
 | 
						|
}
 | 
						|
#else
 | 
						|
#include "mdp_csc_table.h"
 | 
						|
 | 
						|
void mdp_check_tearing(struct mdp_info *mdp, struct msm_mdp_platform_data *pdata)
 | 
						|
{
 | 
						|
	mdp_writel(mdp, pdata->sync_config, MDP_SYNC_CONFIG_0);
 | 
						|
	mdp_writel(mdp, 1, MDP_TEAR_CHECK_EN);
 | 
						|
	mdp_writel(mdp, pdata->sync_thresh, MDP_SYNC_THRESH_0);
 | 
						|
	mdp_writel(mdp, pdata->sync_start_pos, MDP_PRIM_START_POS);
 | 
						|
}
 | 
						|
void mdp_hw_init(struct mdp_info *mdp)
 | 
						|
{
 | 
						|
	int n;
 | 
						|
	int lcdc_enabled;
 | 
						|
 | 
						|
	mdp_irq_mask = 0;
 | 
						|
 | 
						|
	mdp_writel(mdp, 0, MDP_INTR_ENABLE);
 | 
						|
 | 
						|
	/* debug interface write access */
 | 
						|
	mdp_writel(mdp, 1, 0x60);
 | 
						|
	mdp_writel(mdp, 1, MDP_EBI2_PORTMAP_MODE);
 | 
						|
 | 
						|
#ifndef CONFIG_MSM_MDP22
 | 
						|
	lcdc_enabled = mdp_readl(mdp, MDP_LCDC_EN);
 | 
						|
	/* disable lcdc */
 | 
						|
	mdp_writel(mdp, 0, MDP_LCDC_EN);
 | 
						|
	/* enable auto clock gating for all blocks by default */
 | 
						|
	mdp_writel(mdp, 0xffffffff, MDP_CGC_EN);
 | 
						|
	/* reset color/gamma correct parms */
 | 
						|
	mdp_writel(mdp, 0, MDP_DMA_P_COLOR_CORRECT_CONFIG);
 | 
						|
#endif
 | 
						|
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01f8);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01fc);
 | 
						|
	mdp_writel(mdp, 1, 0x60);
 | 
						|
 | 
						|
	for (n = 0; n < ARRAY_SIZE(csc_color_lut); n++)
 | 
						|
		mdp_writel(mdp, csc_color_lut[n].val, csc_color_lut[n].reg);
 | 
						|
 | 
						|
	/* clear up unused fg/main registers */
 | 
						|
	/* comp.plane 2&3 ystride */
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0120);
 | 
						|
 | 
						|
	/* unpacked pattern */
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x012c);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0130);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0134);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0158);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x015c);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0160);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0170);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0174);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x017c);
 | 
						|
 | 
						|
	/* comp.plane 2 & 3 */
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0114);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x0118);
 | 
						|
 | 
						|
	/* clear unused bg registers */
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01c8);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01d0);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01dc);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e0);
 | 
						|
	mdp_writel(mdp, 0, MDP_CMD_DEBUG_ACCESS_BASE + 0x01e4);
 | 
						|
 | 
						|
	for (n = 0; n < ARRAY_SIZE(csc_matrix_config_table); n++)
 | 
						|
		mdp_writel(mdp, csc_matrix_config_table[n].val,
 | 
						|
			   csc_matrix_config_table[n].reg);
 | 
						|
 | 
						|
	mdp_ppp_init_scale(mdp);
 | 
						|
 | 
						|
#ifndef CONFIG_MSM_MDP31
 | 
						|
	mdp_writel(mdp, 0x04000400, MDP_COMMAND_CONFIG);
 | 
						|
#endif
 | 
						|
#ifndef CONFIG_MSM_MDP22
 | 
						|
	if(lcdc_enabled)
 | 
						|
		mdp_writel(mdp, 1, MDP_LCDC_EN);
 | 
						|
#endif
 | 
						|
}
 | 
						|
#endif //CONFIG_MSM_MDP40
 | 
						|
 | 
						|
int mdp_probe(struct platform_device *pdev)
 | 
						|
{
 | 
						|
	struct resource *resource;
 | 
						|
	int ret = -EINVAL;
 | 
						|
	struct mdp_info *mdp;
 | 
						|
	struct msm_mdp_platform_data *pdata = pdev->dev.platform_data;
 | 
						|
 | 
						|
	resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						|
	if (!resource) {
 | 
						|
		pr_err("mdp: can not get mdp mem resource!\n");
 | 
						|
		return -ENOMEM;
 | 
						|
	}
 | 
						|
 | 
						|
	mdp = kzalloc(sizeof(struct mdp_info), GFP_KERNEL);
 | 
						|
	if (!mdp)
 | 
						|
		return -ENOMEM;
 | 
						|
 | 
						|
	spin_lock_init(&mdp->lock);
 | 
						|
 | 
						|
	mdp->irq = platform_get_irq(pdev, 0);
 | 
						|
	if (mdp->irq < 0) {
 | 
						|
		pr_err("mdp: can not get mdp irq\n");
 | 
						|
		ret = mdp->irq;
 | 
						|
		goto error_get_irq;
 | 
						|
	}
 | 
						|
 | 
						|
	mdp->base = ioremap(resource->start,
 | 
						|
			    resource->end - resource->start);
 | 
						|
	if (mdp->base == 0) {
 | 
						|
		printk(KERN_ERR "msmfb: cannot allocate mdp regs!\n");
 | 
						|
		ret = -ENOMEM;
 | 
						|
		goto error_ioremap;
 | 
						|
	}
 | 
						|
 | 
						|
	mdp->mdp_dev.dma = mdp_dma;
 | 
						|
	mdp->mdp_dev.dma_wait = mdp_dma_wait;
 | 
						|
	mdp->mdp_dev.blit = mdp_blit;
 | 
						|
#ifdef CONFIG_FB_MSM_OVERLAY
 | 
						|
	mdp->mdp_dev.overlay_get = mdp4_overlay_get;
 | 
						|
	mdp->mdp_dev.overlay_set = mdp4_overlay_set;
 | 
						|
	mdp->mdp_dev.overlay_unset = mdp4_overlay_unset;
 | 
						|
	mdp->mdp_dev.overlay_play = mdp4_overlay_play;
 | 
						|
#endif
 | 
						|
	mdp->mdp_dev.set_grp_disp = mdp_set_grp_disp;
 | 
						|
	mdp->mdp_dev.set_output_format = mdp_set_output_format;
 | 
						|
	mdp->mdp_dev.check_output_format = mdp_check_output_format;
 | 
						|
	mdp->mdp_dev.configure_dma = mdp_configure_dma;
 | 
						|
 | 
						|
	if (pdata == NULL || pdata->overrides == 0)
 | 
						|
		mdp->mdp_dev.overrides = 0;
 | 
						|
	else if(pdata->overrides)
 | 
						|
		mdp->mdp_dev.overrides = pdata->overrides;
 | 
						|
 | 
						|
	if (pdata == NULL || pdata->color_format == 0)
 | 
						|
		mdp->mdp_dev.color_format = MSM_MDP_OUT_IF_FMT_RGB565;
 | 
						|
	else if(pdata->color_format)
 | 
						|
		mdp->mdp_dev.color_format = pdata->color_format;
 | 
						|
 | 
						|
	if (pdata == NULL || pdata->dma_channel == MDP_DMA_P) {
 | 
						|
#ifdef CONFIG_MSM_MDP40
 | 
						|
		if (mdp->mdp_dev.overrides & MSM_MDP4_MDDI_DMA_SWITCH) {
 | 
						|
			ret = mdp_out_if_register(&mdp->mdp_dev,
 | 
						|
				MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE
 | 
						|
				| MDP_DMA_S_DONE, mdp4_mddi_overlay);
 | 
						|
		} else {
 | 
						|
			ret = mdp_out_if_register(&mdp->mdp_dev,
 | 
						|
				MSM_MDDI_PMDH_INTERFACE, mdp, INTR_OVERLAY0_DONE,
 | 
						|
				mdp4_mddi_overlay);
 | 
						|
		}
 | 
						|
#else
 | 
						|
		ret = mdp_out_if_register(&mdp->mdp_dev,
 | 
						|
				MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_P_DONE,
 | 
						|
				mdp_dma_to_mddi);
 | 
						|
#endif
 | 
						|
	} else if (pdata->dma_channel == MDP_DMA_S) {
 | 
						|
		ret = mdp_out_if_register(&mdp->mdp_dev,
 | 
						|
				MSM_MDDI_PMDH_INTERFACE, mdp, MDP_DMA_S_DONE,
 | 
						|
				mdp_dmas_to_mddi);
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret)
 | 
						|
		goto error_mddi_pmdh_register;
 | 
						|
 | 
						|
	mdp->clk = clk_get(&pdev->dev, "mdp_clk");
 | 
						|
	if (IS_ERR(mdp->clk)) {
 | 
						|
		printk(KERN_INFO "mdp: failed to get mdp clk");
 | 
						|
		ret = PTR_ERR(mdp->clk);
 | 
						|
		goto error_get_mdp_clk;
 | 
						|
	}
 | 
						|
 | 
						|
	mdp->ebi1_clk = clk_get(NULL, "ebi1_clk");
 | 
						|
	if (IS_ERR(mdp->ebi1_clk)) {
 | 
						|
		pr_err("mdp: failed to get ebi1 clk\n");
 | 
						|
		ret = PTR_ERR(mdp->ebi1_clk);
 | 
						|
		goto error_get_ebi1_clk;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	ret = request_irq(mdp->irq, mdp_isr, IRQF_DISABLED, "msm_mdp", mdp);
 | 
						|
	if (ret)
 | 
						|
		goto error_request_irq;
 | 
						|
	disable_irq(mdp->irq);
 | 
						|
 | 
						|
	clk_enable(mdp->clk);
 | 
						|
	mdp_clk_to_disable_later = mdp->clk;
 | 
						|
#ifdef CONFIG_MSM_MDP40
 | 
						|
	//MDP_DISP_INTF_SEL
 | 
						|
	if (mdp_readl(mdp, 0xc0000))
 | 
						|
		mdp_writel(mdp, 0x8, 0x0038);
 | 
						|
	else
 | 
						|
		mdp_writel(mdp, 0xa, 0x0038); //mddi
 | 
						|
	//FIXME: should select mddi or lcdc interface
 | 
						|
	//mdp_writel(mdp, 0x8, 0x0038); //lcdc
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef CONFIG_MSM_MDP40
 | 
						|
extern void mdp4_hw_init(struct mdp_info *mdp);
 | 
						|
	mdp4_hw_init(mdp);
 | 
						|
#else
 | 
						|
	mdp_hw_init(mdp);
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined CONFIG_MSM_MDP302
 | 
						|
	/* enable the tearing check in MDP */
 | 
						|
	if(pdata != NULL && pdata->tearing_check)
 | 
						|
		mdp_check_tearing(mdp, pdata);
 | 
						|
#endif
 | 
						|
	/* register mdp device */
 | 
						|
	mdp->mdp_dev.dev.parent = &pdev->dev;
 | 
						|
	mdp->mdp_dev.dev.class = mdp_class;
 | 
						|
	dev_set_name(&mdp->mdp_dev.dev, "mdp%d", pdev->id);
 | 
						|
 | 
						|
	/* if you can remove the platform device you'd have to implement
 | 
						|
	 * this:
 | 
						|
	mdp_dev.release = mdp_class; */
 | 
						|
 | 
						|
	ret = device_register(&mdp->mdp_dev.dev);
 | 
						|
	if (ret)
 | 
						|
		goto error_device_register;
 | 
						|
 | 
						|
	pr_info("%s: initialized\n", __func__);
 | 
						|
 | 
						|
	return 0;
 | 
						|
 | 
						|
error_device_register:
 | 
						|
	free_irq(mdp->irq, mdp);
 | 
						|
error_request_irq:
 | 
						|
	clk_put(mdp->ebi1_clk);
 | 
						|
error_get_ebi1_clk:
 | 
						|
	clk_put(mdp->clk);
 | 
						|
error_get_mdp_clk:
 | 
						|
error_mddi_pmdh_register:
 | 
						|
	iounmap(mdp->base);
 | 
						|
error_ioremap:
 | 
						|
error_get_irq:
 | 
						|
	kfree(mdp);
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static struct platform_driver msm_mdp_driver = {
 | 
						|
	.probe = mdp_probe,
 | 
						|
	.driver = {.name = "msm_mdp"},
 | 
						|
};
 | 
						|
 | 
						|
static int __init mdp_lateinit(void)
 | 
						|
{
 | 
						|
	if (mdp_clk_to_disable_later)
 | 
						|
		clk_disable(mdp_clk_to_disable_later);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int __init mdp_init(void)
 | 
						|
{
 | 
						|
	mdp_class = class_create(THIS_MODULE, "msm_mdp");
 | 
						|
	if (IS_ERR(mdp_class)) {
 | 
						|
		printk(KERN_ERR "Error creating mdp class\n");
 | 
						|
		return PTR_ERR(mdp_class);
 | 
						|
	}
 | 
						|
	return platform_driver_register(&msm_mdp_driver);
 | 
						|
}
 | 
						|
 | 
						|
subsys_initcall(mdp_init);
 | 
						|
late_initcall(mdp_lateinit);
 |