Commit e9b375b7 authored by Kevin Hilman's avatar Kevin Hilman

Merge branch 'for-next' into master

Conflicts:
	arch/arm/configs/davinci_all_defconfig
	arch/arm/mach-davinci/Kconfig
	arch/arm/mach-davinci/Makefile
	arch/arm/mach-davinci/board-dm355-evm.c
	arch/arm/mach-davinci/board-dm355-leopard.c
	arch/arm/mach-davinci/board-dm644x-evm.c
	arch/arm/mach-davinci/board-dm646x-evm.c
	arch/arm/mach-davinci/board-sffsdr.c
	arch/arm/mach-davinci/clock.c
	arch/arm/mach-davinci/clock.h
	arch/arm/mach-davinci/common.c
	arch/arm/mach-davinci/devices.c
	arch/arm/mach-davinci/dm355.c
	arch/arm/mach-davinci/dm644x.c
	arch/arm/mach-davinci/dm646x.c
	arch/arm/mach-davinci/dma.c
	arch/arm/mach-davinci/gpio.c
	arch/arm/mach-davinci/include/mach/common.h
	arch/arm/mach-davinci/include/mach/dm646x.h
	arch/arm/mach-davinci/include/mach/edma.h
	arch/arm/mach-davinci/include/mach/emac.h
	arch/arm/mach-davinci/include/mach/gpio.h
	arch/arm/mach-davinci/include/mach/mux.h
	arch/arm/mach-davinci/include/mach/uncompress.h
	arch/arm/mach-davinci/irq.c
	arch/arm/mach-davinci/mux.c
	arch/arm/mach-davinci/psc.c
	arch/arm/mach-davinci/serial.c
	arch/arm/mach-davinci/sram.c
	arch/arm/mach-davinci/time.c
	sound/soc/davinci/davinci-evm.c
parents 77bbca13 6d5f14eb
......@@ -16,6 +16,9 @@ NAME = Man-Eating Seals of Antiquity
# o print "Entering directory ...";
MAKEFLAGS += -rR --no-print-directory
# Add custom flags here to avoid conflict with updates
EXTRAVERSION := $(EXTRAVERSION)-davinci1
# We are using a recursive build, so we need to do a little thinking
# to get the ordering right.
#
......@@ -171,6 +174,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ )
SUBARCH := arm
# Cross compiling and selecting different set of gcc/bin-utils
# ---------------------------------------------------------------------------
#
......@@ -191,7 +196,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
export KBUILD_BUILDHOST := $(SUBARCH)
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?=
CROSS_COMPILE ?= arm-linux-
# Architecture as present in compile.h
UTS_MACHINE := $(ARCH)
......
......@@ -139,6 +139,10 @@ endif
machine-$(CONFIG_ARCH_H720X) := h720x
machine-$(CONFIG_ARCH_AAEC2000) := aaec2000
machine-$(CONFIG_ARCH_REALVIEW) := realview
machine-$(CONFIG_ARCH_AT91) := at91rm9200
machine-$(CONFIG_ARCH_EP93XX) := ep93xx
machine-$(CONFIG_ARCH_PNX4008) := pnx4008
machine-$(CONFIG_ARCH_NETX) := netx
machine-$(CONFIG_ARCH_AT91) := at91
machine-$(CONFIG_ARCH_EP93XX) := ep93xx
machine-$(CONFIG_ARCH_PNX4008) := pnx4008
......
......@@ -48,6 +48,10 @@ else
endif
endif
ifeq ($(CONFIG_ARCH_DAVINCI),y)
OBJS += head-davinci.o
endif
#
# We now have a PIC decompressor implementation. Decompressors running
# from RAM should not define ZTEXTADDR. Decompressors running directly
......
/*
* DaVinci (and relatives) specific tweaks.
* This is merged into head.S by the linker.
*/
#include <linux/linkage.h>
#include <asm/mach-types.h>
.section ".start", "ax"
__davinci_start:
/* Save machine number for later conditional code */
adr r0, davinci_machine_no
str r7, [r0]
.globl davinci_machine_no
davinci_machine_no:
.word 0x00000000
......@@ -487,7 +487,7 @@ CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_CFI_I4 is not set
# CONFIG_MTD_CFI_I8 is not set
# CONFIG_MTD_CFI_INTELEXT is not set
CONFIG_MTD_CFI_INTELEXT=m
CONFIG_MTD_CFI_AMDSTD=m
# CONFIG_MTD_CFI_STAA is not set
CONFIG_MTD_CFI_UTIL=m
......@@ -735,10 +735,7 @@ CONFIG_INPUT=y
#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=m
CONFIG_INPUT_MOUSEDEV_PSAUX=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_MOUSEDEV is not set
# CONFIG_INPUT_JOYDEV is not set
CONFIG_INPUT_EVDEV=m
CONFIG_INPUT_EVBUG=m
......@@ -746,33 +743,11 @@ CONFIG_INPUT_EVBUG=m
#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
CONFIG_KEYBOARD_ATKBD=m
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_LKKBD is not set
CONFIG_KEYBOARD_XTKBD=m
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
CONFIG_KEYBOARD_GPIO=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_AD7879_I2C is not set
# CONFIG_TOUCHSCREEN_AD7879 is not set
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_ELO is not set
# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
# CONFIG_TOUCHSCREEN_MTOUCH is not set
# CONFIG_TOUCHSCREEN_INEXIO is not set
# CONFIG_TOUCHSCREEN_MK712 is not set
# CONFIG_TOUCHSCREEN_PENMOUNT is not set
# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
# CONFIG_TOUCHSCREEN_TSC2007 is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
# CONFIG_INPUT_MISC is not set
#
......@@ -814,8 +789,7 @@ CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
# CONFIG_IPMI_HANDLER is not set
CONFIG_HW_RANDOM=m
# CONFIG_HW_RANDOM_TIMERIOMEM is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_R3964 is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_TCG_TPM is not set
......@@ -875,7 +849,7 @@ CONFIG_GPIOLIB=y
#
# CONFIG_GPIO_MAX732X is not set
# CONFIG_GPIO_PCA953X is not set
CONFIG_GPIO_PCF857X=m
CONFIG_GPIO_PCF857X=y
#
# PCI GPIO expanders:
......@@ -994,44 +968,41 @@ CONFIG_SSB_POSSIBLE=y
#
# Multimedia core support
#
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_COMMON=y
CONFIG_VIDEO_ALLOW_V4L1=y
CONFIG_VIDEO_V4L1_COMPAT=y
CONFIG_VIDEO_DEV=m
CONFIG_VIDEO_V4L2_COMMON=m
# CONFIG_VIDEO_ALLOW_V4L1 is not set
# CONFIG_VIDEO_V4L1_COMPAT is not set
# CONFIG_DVB_CORE is not set
CONFIG_VIDEO_MEDIA=y
CONFIG_VIDEO_MEDIA=m
#
# Multimedia drivers
#
# CONFIG_MEDIA_ATTACH is not set
CONFIG_MEDIA_TUNER=y
CONFIG_MEDIA_TUNER=m
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
CONFIG_MEDIA_TUNER_SIMPLE=y
CONFIG_MEDIA_TUNER_TDA8290=y
CONFIG_MEDIA_TUNER_TDA9887=y
CONFIG_MEDIA_TUNER_TEA5761=y
CONFIG_MEDIA_TUNER_TEA5767=y
CONFIG_MEDIA_TUNER_MT20XX=y
CONFIG_MEDIA_TUNER_XC2028=y
CONFIG_MEDIA_TUNER_XC5000=y
CONFIG_MEDIA_TUNER_MC44S803=y
CONFIG_VIDEO_V4L2=y
CONFIG_VIDEO_V4L1=y
CONFIG_MEDIA_TUNER_SIMPLE=m
CONFIG_MEDIA_TUNER_TDA8290=m
CONFIG_MEDIA_TUNER_TDA9887=m
CONFIG_MEDIA_TUNER_TEA5761=m
CONFIG_MEDIA_TUNER_TEA5767=m
CONFIG_MEDIA_TUNER_MT20XX=m
CONFIG_MEDIA_TUNER_XC2028=m
CONFIG_MEDIA_TUNER_XC5000=m
CONFIG_MEDIA_TUNER_MC44S803=m
CONFIG_VIDEO_V4L2=m
CONFIG_VIDEO_CAPTURE_DRIVERS=y
# CONFIG_VIDEO_ADV_DEBUG is not set
# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
# CONFIG_VIDEO_VIVI is not set
# CONFIG_VIDEO_CPIA is not set
# CONFIG_VIDEO_CPIA2 is not set
# CONFIG_VIDEO_TVP5146 is not set
# CONFIG_VIDEO_SAA5246A is not set
# CONFIG_VIDEO_SAA5249 is not set
# CONFIG_SOC_CAMERA is not set
# CONFIG_V4L_USB_DRIVERS is not set
# CONFIG_RADIO_ADAPTERS is not set
CONFIG_DAB=y
# CONFIG_USB_DABUSB is not set
# CONFIG_DAB is not set
#
# Graphics support
......@@ -1042,9 +1013,9 @@ CONFIG_FB=y
CONFIG_FIRMWARE_EDID=y
# CONFIG_FB_DDC is not set
# CONFIG_FB_BOOT_VESA_SUPPORT is not set
# CONFIG_FB_CFB_FILLRECT is not set
# CONFIG_FB_CFB_COPYAREA is not set
# CONFIG_FB_CFB_IMAGEBLIT is not set
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
# CONFIG_FB_SYS_FILLRECT is not set
# CONFIG_FB_SYS_COPYAREA is not set
......@@ -1061,6 +1032,7 @@ CONFIG_FIRMWARE_EDID=y
# Frame buffer hardware drivers
#
# CONFIG_FB_S1D13XXX is not set
CONFIG_FB_DAVINCI=y
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FB_METRONOME is not set
# CONFIG_FB_MB862XX is not set
......@@ -1112,9 +1084,13 @@ CONFIG_SND_USB=y
# CONFIG_SND_USB_AUDIO is not set
# CONFIG_SND_USB_CAIAQ is not set
CONFIG_SND_SOC=m
# CONFIG_SND_DAVINCI_SOC is not set
CONFIG_SND_DAVINCI_SOC=m
CONFIG_SND_DAVINCI_SOC_I2S=m
CONFIG_SND_DAVINCI_SOC_EVM=m
# CONFIG_SND_DAVINCI_SOC_SFFSDR is not set
CONFIG_SND_SOC_I2C_AND_SPI=m
# CONFIG_SND_SOC_ALL_CODECS is not set
CONFIG_SND_SOC_TLV320AIC3X=m
# CONFIG_SOUND_PRIME is not set
CONFIG_HID_SUPPORT=y
CONFIG_HID=m
......@@ -1328,7 +1304,7 @@ CONFIG_MMC_BLOCK=m
# MMC/SD/SDIO Host Controller Drivers
#
# CONFIG_MMC_SDHCI is not set
# CONFIG_MMC_DAVINCI is not set
CONFIG_MMC_DAVINCI=m
# CONFIG_MEMSTICK is not set
# CONFIG_ACCESSIBILITY is not set
CONFIG_NEW_LEDS=y
......@@ -1373,6 +1349,7 @@ CONFIG_RTC_INTF_DEV=y
#
# I2C RTC drivers
#
# CONFIG_RTC_DRV_DAVINCI_EVM is not set
# CONFIG_RTC_DRV_DS1307 is not set
# CONFIG_RTC_DRV_DS1374 is not set
# CONFIG_RTC_DRV_DS1672 is not set
......
......@@ -62,7 +62,7 @@ void __init davinci_init_i2c(struct davinci_i2c_platform_data *pdata)
#if defined(CONFIG_MMC_DAVINCI) || defined(CONFIG_MMC_DAVINCI_MODULE)
static u64 mmcsd0_dma_mask = DMA_32BIT_MASK;
static u64 mmcsd0_dma_mask = DMA_BIT_MASK(32);
static struct resource mmcsd0_resources[] = {
{
......@@ -95,13 +95,13 @@ static struct platform_device davinci_mmcsd0_device = {
.id = 0,
.dev = {
.dma_mask = &mmcsd0_dma_mask,
.coherent_dma_mask = DMA_32BIT_MASK,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.num_resources = ARRAY_SIZE(mmcsd0_resources),
.resource = mmcsd0_resources,
};
static u64 mmcsd1_dma_mask = DMA_32BIT_MASK;
static u64 mmcsd1_dma_mask = DMA_BIT_MASK(32);
static struct resource mmcsd1_resources[] = {
{
......@@ -132,7 +132,7 @@ static struct platform_device davinci_mmcsd1_device = {
.id = 1,
.dev = {
.dma_mask = &mmcsd1_dma_mask,
.coherent_dma_mask = DMA_32BIT_MASK,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.num_resources = ARRAY_SIZE(mmcsd1_resources),
.resource = mmcsd1_resources,
......
......@@ -11,8 +11,7 @@
#ifndef __ASM_ARCH_SYSTEM_H
#define __ASM_ARCH_SYSTEM_H
#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/io.h>
extern void davinci_watchdog_reset(void);
......
......@@ -12,6 +12,6 @@
#define __ASM_ARCH_TIMEX_H
/* The source frequency for the timers is the 27MHz clock */
#define CLOCK_TICK_RATE 27000000
#define CLOCK_TICK_RATE 27000000
#endif /* __ASM_ARCH_TIMEX_H__ */
#endif /* __ASM_ARCH_TIMEX_H__ */
......@@ -112,6 +112,7 @@ struct davinci_i2c_dev {
u8 *buf;
size_t buf_len;
int irq;
int stop;
u8 terminate;
struct i2c_adapter adapter;
};
......@@ -187,6 +188,11 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh);
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKL_REG, clkl);
/* Respond at reserved "SMBus Host" slave address" (and zero);
* we seem to have no option to not respond...
*/
davinci_i2c_write_reg(dev, DAVINCI_I2C_OAR_REG, 0x08);
dev_dbg(dev->dev, "input_clock = %d, CLK = %d\n", input_clock, clk);
dev_dbg(dev->dev, "PSC = %d\n",
davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG));
......@@ -244,9 +250,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
u16 w;
int r;
if (msg->len == 0)
return -EINVAL;
if (!pdata)
pdata = &davinci_i2c_platform_data_default;
/* Introduce a delay, required for some boards (e.g Davinci EVM) */
......@@ -258,6 +261,7 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
dev->buf = msg->buf;
dev->buf_len = msg->len;
dev->stop = stop;
davinci_i2c_write_reg(dev, DAVINCI_I2C_CNT_REG, dev->buf_len);
......@@ -275,6 +279,10 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
flag |= DAVINCI_I2C_MDR_TRX;
if (stop)
flag |= DAVINCI_I2C_MDR_STP;
if (msg->len == 0) {
flag |= DAVINCI_I2C_MDR_RM;
flag &= ~DAVINCI_I2C_MDR_STP;
}
/* Enable receive or transmit interrupts */
w = davinci_i2c_read_reg(dev, DAVINCI_I2C_IMR_REG);
......@@ -285,6 +293,16 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w);
dev->terminate = 0;
/* First byte should be set here, not after interrupt,
* because transmit-data-ready interrupt can come before
* NACK-interrupt during sending of previous message and
* ICDXR may have wrong data */
if ((!(msg->flags & I2C_M_RD)) && dev->buf_len) {
davinci_i2c_write_reg(dev, DAVINCI_I2C_DXR_REG, *dev->buf++);
dev->buf_len--;
}
/* write the data into mode register */
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
......@@ -366,7 +384,7 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
static u32 i2c_davinci_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
static void terminate_read(struct davinci_i2c_dev *dev)
......@@ -387,7 +405,7 @@ static void terminate_write(struct davinci_i2c_dev *dev)
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
if (!dev->terminate)
dev_err(dev->dev, "TDR IRQ while no data to send\n");
dev_dbg(dev->dev, "TDR IRQ while no data to send\n");
}
/*
......@@ -425,6 +443,14 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
case DAVINCI_I2C_IVR_ARDY:
davinci_i2c_write_reg(dev,
DAVINCI_I2C_STR_REG, DAVINCI_I2C_STR_ARDY);
if (((dev->buf_len == 0) && (dev->stop != 0)) ||
(dev->cmd_err & DAVINCI_I2C_STR_NACK)) {
w = davinci_i2c_read_reg(dev,
DAVINCI_I2C_MDR_REG);
MOD_REG_BIT(w, DAVINCI_I2C_MDR_STP, 1);
davinci_i2c_write_reg(dev,
DAVINCI_I2C_MDR_REG, w);
}
complete(&dev->cmd_complete);
break;
......@@ -473,9 +499,14 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
break;
case DAVINCI_I2C_IVR_AAS:
dev_warn(dev->dev, "Address as slave interrupt\n");
}/* switch */
}/* while */
dev_dbg(dev->dev, "Address as slave interrupt\n");
break;
default:
dev_warn(dev->dev, "Unrecognized irq stat %d\n", stat);
break;
}
}
return count ? IRQ_HANDLED : IRQ_NONE;
}
......@@ -523,7 +554,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
dev->irq = irq->start;
platform_set_drvdata(pdev, dev);
dev->clk = clk_get(&pdev->dev, "I2CCLK");
dev->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dev->clk)) {
r = -ENODEV;
goto err_free_mem;
......
/*
* linux/drivers/i2c/busses/davinci/i2c_davinci.h
*
* Copyright (C) 2006 Texas Instruments.
*
* ----------------------------------------------------------------------------
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ----------------------------------------------------------------------------
Modifications:
ver. 1.0: Feb 2005, Vinod/Sudhakar
-
*
*/
#define DAVINCI_I2C_ICOAR_OADDR_MASK (0x03FFu)
#define DAVINCI_I2C_ICIMR_AAS_MASK (0x0040u)
#define DAVINCI_I2C_ICIMR_SCD_MASK (0x0020u)
#define DAVINCI_I2C_ICIMR_ICXRDY_MASK (0x0010u)
#define DAVINCI_I2C_ICIMR_ICRRDY_MASK (0x0008u)
#define DAVINCI_I2C_ICIMR_ARDY_MASK (0x0004u)
#define DAVINCI_I2C_ICIMR_NACK_MASK (0x0002u)
#define DAVINCI_I2C_ICIMR_AL_MASK (0x0001u)
#define DAVINCI_I2C_ICSTR_SDIR_MASK (0x4000u)
#define DAVINCI_I2C_ICSTR_NACKSNT_MASK (0x2000u)
#define DAVINCI_I2C_ICSTR_BB_MASK (0x1000u)
#define DAVINCI_I2C_ICSTR_RSFULL_MASK (0x0800u)
#define DAVINCI_I2C_ICSTR_XSMT_MASK (0x0400u)
#define DAVINCI_I2C_ICSTR_AAS_MASK (0x0200u)
#define DAVINCI_I2C_ICSTR_AD0_MASK (0x0100u)
#define DAVINCI_I2C_ICSTR_SCD_MASK (0x0020u)
#define DAVINCI_I2C_ICSTR_ICXRDY_MASK (0x0010u)
#define DAVINCI_I2C_ICSTR_ICRRDY_MASK (0x0008u)
#define DAVINCI_I2C_ICSTR_ARDY_MASK (0x0004u)
#define DAVINCI_I2C_ICSTR_NACK_MASK (0x0002u)
#define DAVINCI_I2C_ICSTR_AL_MASK (0x0001u)
#define DAVINCI_I2C_ICCLKL_ICCL_MASK (0xFFFFu)
#define DAVINCI_I2C_ICCLKH_ICCH_MASK (0xFFFFu)
#define DAVINCI_I2C_ICCNT_ICDC_MASK (0xFFFFu)
#define DAVINCI_I2C_ICDRR_D_MASK (0x00FFu)
#define DAVINCI_I2C_ICSAR_SADDR_MASK (0x03FFu)
#define DAVINCI_I2C_ICDXR_D_MASK (0x00FFu)
#define DAVINCI_I2C_ICMDR_NACKMOD_MASK (0x8000u)
#define DAVINCI_I2C_ICMDR_FREE_MASK (0x4000u)
#define DAVINCI_I2C_ICMDR_STT_MASK (0x2000u)
#define DAVINCI_I2C_ICMDR_STP_MASK (0x0800u)
#define DAVINCI_I2C_ICMDR_MST_MASK (0x0400u)
#define DAVINCI_I2C_ICMDR_TRX_MASK (0x0200u)
#define DAVINCI_I2C_ICMDR_XA_MASK (0x0100u)
#define DAVINCI_I2C_ICMDR_RM_MASK (0x0080u)
#define DAVINCI_I2C_ICMDR_DLB_MASK (0x0040u)
#define DAVINCI_I2C_ICMDR_IRS_MASK (0x0020u)
#define DAVINCI_I2C_ICMDR_STB_MASK (0x0010u)
#define DAVINCI_I2C_ICMDR_FDF_MASK (0x0008u)
#define DAVINCI_I2C_ICMDR_BC_MASK (0x0007u)
#define DAVINCI_I2C_ICIVR_TESTMD_MASK (0x0F00u)
#define DAVINCI_I2C_ICIVR_INTCODE_MASK (0x0007u)
#define DAVINCI_I2C_ICIVR_INTCODE_NONE (0x0000u)
#define DAVINCI_I2C_ICIVR_INTCODE_AL (0x0001u)
#define DAVINCI_I2C_ICIVR_INTCODE_NACK (0x0002u)
#define DAVINCI_I2C_ICIVR_INTCODE_RAR (0x0003u)
#define DAVINCI_I2C_ICIVR_INTCODE_RDR (0x0004u)
#define DAVINCI_I2C_ICIVR_INTCODE_TDR (0x0005u)
#define DAVINCI_I2C_ICIVR_INTCODE_SCD (0x0006u)
#define DAVINCI_I2C_ICIVR_INTCODE_AAS (0x0007u)
#define DAVINCI_I2C_ICEMDR_BCM_MASK (0x0001u)
#define DAVINCI_I2C_ICPSC_IPSC_MASK (0x00FFu)
#define DAVINCI_I2C_ICPID1_CLASS_MASK (0xFF00u)
#define DAVINCI_I2C_ICPID1_REVISION_MASK (0x00FFu)
#define DAVINCI_I2C_ICPID2_TYPE_MASK (0x00FFu)
#define DAVINCI_I2C_ICPFUNC_PFUNC_MASK (0x00000001u)
#define DAVINCI_I2C_ICPDIR_PDIR1_MASK (0x00000002u)
#define DAVINCI_I2C_ICPDIR_PDIR0_MASK (0x00000001u)
#define DAVINCI_I2C_ICPDIN_PDIN1_MASK (0x00000002u)
#define DAVINCI_I2C_ICPDIN_PDIN0_MASK (0x00000001u)
#define DAVINCI_I2C_ICPDOUT_PDOUT1_MASK (0x00000002u)
#define DAVINCI_I2C_ICPDOUT_PDOUT0_MASK (0x00000001u)
#define DAVINCI_I2C_ICPDSET_PDSET1_MASK (0x00000002u)
#define DAVINCI_I2C_ICPDSET_PDSET0_MASK (0x00000001u)
#define DAVINCI_I2C_ICPDCLR_PDCLR1_MASK (0x00000002u)
#define DAVINCI_I2C_ICPDCLR_PDCLR0_MASK (0x00000001u)
/**************************************************************************\
* Register Overlay Structure
\**************************************************************************/
typedef struct {
u16 icoar;
u8 rsvd0[2];
u16 icimr;
u8 rsvd1[2];
u16 icstr;
u8 rsvd2[2];
u16 icclkl;
u8 rsvd3[2];
u16 icclkh;
u8 rsvd4[2];
u16 iccnt;
u8 rsvd5[2];
u16 icdrr;
u8 rsvd6[2];
u16 icsar;
u8 rsvd7[2];
u16 icdxr;
u8 rsvd8[2];
u16 icmdr;
u8 rsvd9[2];
u16 icivr;
u8 rsvd10[2];
u16 icemdr;
u8 rsvd11[2];
u16 icpsc;
u8 rsvd12[2];
u16 icpid1;
u8 rsvd13[2];
u16 icpid2;
u8 rsvd14[14];
u32 ipcfunc;
u32 icpdir;
u32 icpdin;
u32 icpdout;
u32 icpdset;
u32 icpdclr;
} davinci_i2cregs;
/**************************************************************************\
* Overlay structure typedef definition
\**************************************************************************/
typedef volatile davinci_i2cregs *davinci_i2cregsovly;
struct i2c_davinci_device {
int cmd_err;
struct completion cmd_complete;
wait_queue_head_t cmd_wait;
u8 *buf;
size_t buf_len;
davinci_i2cregsovly regs;
int irq;
struct i2c_adapter adapter;
struct clk *clk;
struct device *dev;
};
......@@ -20,4 +20,3 @@ obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
endif
......@@ -317,7 +317,7 @@ static int __init palm_bk3710_probe(struct platform_device *pdev)
int i, rc;
hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL };
clk = clk_get(&pdev->dev, "IDECLK");
clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(clk))
return -ENODEV;
......
......@@ -332,4 +332,12 @@ config KEYBOARD_SH_KEYSC
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
config KEYBOARD_DM355EVM
tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
depends on MFD_DM355EVM_MSP
help
Supports the pushbuttons and IR remote used with
the DM355 EVM board.
endif
......@@ -28,3 +28,4 @@ obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_DM355EVM) += dm355evm_keys.o
/*
* dm355evm_keys.c - support buttons and IR remote on DM355 EVM board
*
* Copyright (c) 2008 by David Brownell
*
* 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; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/i2c/dm355evm_msp.h>
/*
* The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons
* and an IR receptor used for the remote control. When any key is
* pressed, or its autorepeat kicks in, an event is sent. This driver
* read those events from the small (32 event) queue and reports them.
*
* Because we communicate with the MSP430 using I2C, and all I2C calls
* in Linux sleep, we need to cons up a kind of threaded IRQ handler
* using a work_struct. The IRQ is active low, but we use it through
* the GPIO controller so we can trigger on falling edges.
*
* Note that physically there can only be one of these devices.
*
* This driver was tested with firmware revision A4.
*/
struct dm355evm_keys {
struct work_struct work;
struct input_dev *input;
struct device *dev;
int irq;
};
static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
{
struct dm355evm_keys *keys = _keys;
schedule_work(&keys->work);
return IRQ_HANDLED;
}
/* These initial keycodes can be remapped by dm355evm_setkeycode(). */
static struct {
u16 event;
u16 keycode;
} dm355evm_keys[] = {
/*
* Pushbuttons on the EVM board ... note that the labels for these
* are SW10/SW11/etc on the PC board. The left/right orientation
* comes only from the firmware's documentation, and presumes the
* power connector is immediately in front of you and the IR sensor
* is to the right. (That is, rotate the board counter-clockwise
* by 90 degrees from the SW10/etc and "DM355 EVM" labels.)
*/
{ 0x00d8, KEY_OK, }, /* SW12 */
{ 0x00b8, KEY_UP, }, /* SW13 */
{ 0x00e8, KEY_DOWN, }, /* SW11 */
{ 0x0078, KEY_LEFT, }, /* SW14 */
{ 0x00f0, KEY_RIGHT, }, /* SW10 */
/*
* IR buttons ... codes assigned to match the universal remote
* provided with the EVM (Philips PM4S) using DVD code 0020.
*
* These event codes match firmware documentation, but other
* remote controls could easily send more RC5-encoded events.
* The PM4S manual was used in several cases to help select
* a keycode reflecting the intended usage.
*
* RC5 codes are 14 bits, with two start bits (0x3 prefix)
* and a toggle bit (masked out below).
*/
{ 0x300c, KEY_POWER, }, /* NOTE: docs omit this */
{ 0x3000, KEY_NUMERIC_0, },
{ 0x3001, KEY_NUMERIC_1, },
{ 0x3002, KEY_NUMERIC_2, },
{ 0x3003, KEY_NUMERIC_3, },
{ 0x3004, KEY_NUMERIC_4, },
{ 0x3005, KEY_NUMERIC_5, },
{ 0x3006, KEY_NUMERIC_6, },
{ 0x3007, KEY_NUMERIC_7, },
{ 0x3008, KEY_NUMERIC_8, },
{ 0x3009, KEY_NUMERIC_9, },
{ 0x3022, KEY_ENTER, },
{ 0x30ec, KEY_MODE, }, /* "tv/vcr/..." */
{ 0x300f, KEY_SELECT, }, /* "info" */
{ 0x3020, KEY_CHANNELUP, }, /* "up" */
{ 0x302e, KEY_MENU, }, /* "in/out" */
{ 0x3011, KEY_VOLUMEDOWN, }, /* "left" */
{ 0x300d, KEY_MUTE, }, /* "ok" */
{ 0x3010, KEY_VOLUMEUP, }, /* "right" */
{ 0x301e, KEY_SUBTITLE, }, /* "cc" */
{ 0x3021, KEY_CHANNELDOWN, }, /* "down" */
{ 0x3022, KEY_PREVIOUS, },
{ 0x3026, KEY_SLEEP, },
{ 0x3172, KEY_REWIND, }, /* NOTE: docs wrongly say 0x30ca */
{ 0x3175, KEY_PLAY, },
{ 0x3174, KEY_FASTFORWARD, },
{ 0x3177, KEY_RECORD, },
{ 0x3176, KEY_STOP, },
{ 0x3169, KEY_PAUSE, },
};
static void dm355evm_keys_work(struct work_struct *work)
{
struct dm355evm_keys *keys;
int status;
keys = container_of(work, struct dm355evm_keys, work);
/* For simplicity we ignore INPUT_COUNT and just read
* events until we get the "queue empty" indicator.
* Reading INPUT_LOW decrements the count.
*/
for (;;) {
static u16 last_event;
u16 event;
int keycode;
int i;
status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH);
if (status < 0) {
dev_dbg(keys->dev, "input high err %d\n",
status);
break;
}
event = status << 8;
status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW);
if (status < 0) {
dev_dbg(keys->dev, "input low err %d\n",
status);
break;
}
event |= status;
if (event == 0xdead)
break;
/* Press and release a button: two events, same code.
* Press and hold (autorepeat), then release: N events
* (N > 2), same code. For RC5 buttons the toggle bits
* distinguish (for example) "1-autorepeat" from "1 1";
* but PCB buttons don't support that bit.
*
* So we must synthesize release events. We do that by
* mapping events to a press/release event pair; then
* to avoid adding extra events, skip the second event
* of each pair.
*/
if (event == last_event) {
last_event = 0;
continue;
}
last_event = event;
/* ignore the RC5 toggle bit */
event &= ~0x0800;
/* find the key, or leave it as unknown */
keycode = KEY_UNKNOWN;
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
if (dm355evm_keys[i].event != event)
continue;
keycode = dm355evm_keys[i].keycode;
break;
}
dev_dbg(keys->dev,
"input event 0x%04x--> keycode %d\n",
event, keycode);
/* report press + release */
input_report_key(keys->input, keycode, 1);
input_sync(keys->input);
input_report_key(keys->input, keycode, 0);
input_sync(keys->input);
}
}
static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode)
{
u16 old_keycode;
unsigned i;
if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
return -EINVAL;
old_keycode = dm355evm_keys[index].keycode;
dm355evm_keys[index].keycode = keycode;
set_bit(keycode, dev->keybit);
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
if (dm355evm_keys[index].keycode == old_keycode)
goto done;
}
clear_bit(old_keycode, dev->keybit);
done:
return 0;
}
static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode)
{
if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
return -EINVAL;
return dm355evm_keys[index].keycode;
}
/*----------------------------------------------------------------------*/
static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
{
struct dm355evm_keys *keys;
int status = -ENOMEM;
struct input_dev *input;
int i;
/* allocate instance struct */
keys = kzalloc(sizeof *keys, GFP_KERNEL);
if (!keys)
goto fail0;
platform_set_drvdata(pdev, keys);
keys->dev = &pdev->dev;
/* ... and input dev ... */
input = input_allocate_device();
if (!input)
goto fail0;
keys->input = input;
input_set_drvdata(input, keys);
input->name = "DM355 EVM Controls";
input->phys = "dm355evm/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_I2C;
input->id.product = 0x0355;
input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
input->evbit[0] = BIT(EV_KEY);
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++)
set_bit(dm355evm_keys[i].keycode, input->keybit);
input->keycode = dm355evm_keys;
input->setkeycode = dm355evm_setkeycode;
input->getkeycode = dm355evm_getkeycode;
/* set up "threaded IRQ handler" */
status = platform_get_irq(pdev, 0);
if (status < 0)
goto fail1;
keys->irq = status;
INIT_WORK(&keys->work, dm355evm_keys_work);
/* REVISIT: flush the event queue? */
/* register */
status = input_register_device(input);
if (status < 0)
goto fail0;
/* start reporting events */
status = request_irq(keys->irq, dm355evm_keys_irq,
IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), keys);
if (status < 0) {
input_unregister_device(input);
goto fail1;
}
return 0;
fail1:
input_free_device(input);
fail0:
kfree(keys);
dev_err(&pdev->dev, "can't register, err %d\n", status);
return status;
}
static int __devexit dm355evm_keys_remove(struct platform_device *pdev)
{
struct dm355evm_keys *keys = platform_get_drvdata(pdev);
free_irq(keys->irq, keys);
input_unregister_device(keys->input);
kfree(keys);
return 0;
}
/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should
* be able to wake up the system. When device_may_wakeup(&pdev->dev), call
* enable_irq_wake() on suspend, and disable_irq_wake() on resume.
*/
/*
* I2C is used to talk to the MSP430, but this platform device is
* exposed by an MFD driver that manages I2C communications.
*/
static struct platform_driver dm355evm_keys_driver = {
.probe = dm355evm_keys_probe,
.remove = __devexit_p(dm355evm_keys_remove),
.driver = {
.owner = THIS_MODULE,
.name = "dm355evm_keys",
},
};
static int __init dm355evm_keys_init(void)
{
return platform_driver_register(&dm355evm_keys_driver);
}
module_init(dm355evm_keys_init);
static void __exit dm355evm_keys_exit(void)
{
platform_driver_unregister(&dm355evm_keys_driver);
}
module_exit(dm355evm_keys_exit);
MODULE_LICENSE("GPL");
......@@ -479,6 +479,26 @@ config VIDEO_VIVI
Say Y here if you want to test video apps or debug V4L devices.
In doubt, say N.
config VIDEO_TVP5146
tristate "TVP5146 video decoder"
depends on I2C && ARCH_DAVINCI
help
Support for I2C bus based TVP5146 configuration.
To compile this driver as a module, choose M here: the
module will be called tvp5146.
config VIDEO_DAVINCI
tristate "Davinci Video Capture"
depends on VIDEO_DEV && VIDEO_TVP5146 && ARCH_DAVINCI
select VIDEOBUF_GEN
select VIDEOBUF_DMA_SG
help
Support for Davinci based frame grabber through CCDC.
To compile this driver as a module, choose M here: the
module will be called vpfe.
source "drivers/media/video/bt8xx/Kconfig"
config VIDEO_PMS
......
......@@ -10,6 +10,8 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o
omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
davinci-vpfe-objs := ccdc_davinci.o davinci_vpfe.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
......@@ -131,6 +133,9 @@ obj-$(CONFIG_USB_S2255) += s2255drv.o
obj-$(CONFIG_VIDEO_IVTV) += ivtv/
obj-$(CONFIG_VIDEO_CX18) += cx18/
obj-$(CONFIG_VIDEO_DAVINCI) += davinci-vpfe.o
obj-$(CONFIG_VIDEO_TVP5146) += tvp5146.o
obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
......
/*
*
*
* Copyright (C) 2006 Texas Instruments Inc
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* ccdc_davinci.c */
#include <media/ccdc_davinci.h>
#define debug_print(x...) //printk(x)
void ccdc_reset()
{
int i;
/* disable CCDC */
ccdc_enable(0);
/* set all registers to default value */
for (i = 0; i <= 0x94; i += 4) {
regw(0, i);
}
regw(0, PCR);
regw(0, SYN_MODE);
regw(0, HD_VD_WID);
regw(0, PIX_LINES);
regw(0, HORZ_INFO);
regw(0, VERT_START);
regw(0, VERT_LINES);
regw(0xffff00ff, CULLING);
regw(0, HSIZE_OFF);
regw(0, SDOFST);
regw(0, SDR_ADDR);
regw(0, VDINT);
regw(0, REC656IF);
regw(0, CCDCFG);
regw(0, FMTCFG);
regw(0, VP_OUT);
}
void ccdc_setwin(ccdc_params_ycbcr * params)
{
int horz_start, horz_nr_pixels;
int vert_start, vert_nr_lines;
/* configure horizonal and vertical starts and sizes */
horz_start = params->win.left << 1;
horz_nr_pixels = (params->win.width <<1) - 1;
regw((horz_start << 16) | horz_nr_pixels, HORZ_INFO);
vert_start = params->win.top;
if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) {
vert_nr_lines = (params->win.height >> 1) - 1;
vert_start >>= 1;
} else {
vert_nr_lines = params->win.height - 1;
}
regw((vert_start << 16) | vert_start, VERT_START);
regw(vert_nr_lines, VERT_LINES);
}
void ccdc_config_ycbcr(ccdc_params_ycbcr * params)
{
u32 syn_mode;
/* first reset the CCDC */
/* all registers have default values after reset */
/* This is important since we assume default values to be set in */
/* a lot of registers that we didn't touch */
ccdc_reset();
/* configure pixel format */
syn_mode = (params->pix_fmt & 0x3) << 12;
/* configure video frame format */
syn_mode |= (params->frm_fmt & 0x1) << 7;
/* setup BT.656 sync mode */
if (params->bt656_enable) {
regw(3, REC656IF);
/* configure the FID, VD, HD pin polarity */
/* fld,hd pol positive, vd negative, 8-bit pack mode */
syn_mode |= 0x00000F04;
} else {/* y/c external sync mode */
syn_mode |= ((params->fid_pol & 0x1) << 4);
syn_mode |= ((params->hd_pol & 0x1) << 3);
syn_mode |= ((params->vd_pol & 0x1) << 2);
}
/* configure video window */
ccdc_setwin(params);
/* configure the order of y cb cr in SD-RAM */
regw((params->pix_order << 11) | 0x8000, CCDCFG);
/* configure the horizontal line offset */
/* this is done by rounding up width to a multiple of 16 pixels */
/* and multiply by two to account for y:cb:cr 4:2:2 data */
regw(((params->win.width * 2) + 31) & 0xffffffe0, HSIZE_OFF);
/* configure the memory line offset */
if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
/* two fields are interleaved in memory */
regw(0x00000249, SDOFST);
}
/* enable output to SDRAM */
syn_mode |= (0x1 << 17);
/* enable internal timing generator */
syn_mode |= (0x1 << 16);
regw(syn_mode, SYN_MODE);
}
/*
*
*
* Copyright (C) 2006 Texas Instruments Inc
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* davinci_vpfe.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/kdev_t.h>
#include <linux/string.h>
#include <linux/videodev.h>
#include <linux/wait.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <asm/io.h>
#include <asm/dma-mapping.h>
#include <media/davinci_vpfe.h>
#define debug_print(x...) //printk(x)
MODULE_LICENSE("GPL");
static struct v4l2_rect ntsc_bounds = VPFE_WIN_NTSC;
static struct v4l2_rect pal_bounds = VPFE_WIN_PAL;
static struct v4l2_fract ntsc_aspect = VPFE_PIXELASPECT_NTSC;
static struct v4l2_fract pal_aspect = VPFE_PIXELASPECT_PAL;
static struct v4l2_rect ntscsp_bounds = VPFE_WIN_NTSC_SP;
static struct v4l2_rect palsp_bounds = VPFE_WIN_PAL_SP;
static struct v4l2_fract sp_aspect = VPFE_PIXELASPECT_NTSC_SP;
static vpfe_obj vpfe_device = { /* the default format is NTSC */
.usrs = 0,
.io_usrs = 0,
.std = VPFE_STD_AUTO,
.vwin = VPFE_WIN_PAL,
.bounds = VPFE_WIN_PAL,
.pixelaspect = VPFE_PIXELASPECT_NTSC,
.pixelfmt = V4L2_PIX_FMT_UYVY,
.field = V4L2_FIELD_INTERLACED,
.numbuffers = VPFE_DEFNUM_FBUFS,
.ccdc_params = {
.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT,
.frm_fmt = CCDC_FRMFMT_INTERLACED,
.win = VPFE_WIN_PAL,
.fid_pol = CCDC_PINPOL_POSITIVE,
.vd_pol = CCDC_PINPOL_POSITIVE,
.hd_pol = CCDC_PINPOL_POSITIVE,
.bt656_enable = TRUE,
.pix_order = CCDC_PIXORDER_CBYCRY,
.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED
},
.tvp5146_params = {
.mode = TVP5146_MODE_AUTO,
.amuxmode = TVP5146_AMUX_COMPOSITE,
.enablebt656sync = TRUE
},
.irqlock = SPIN_LOCK_UNLOCKED
};
struct v4l2_capability vpfe_drvcap = {
.driver = "vpfe driver",
.card = "DaVinci EVM",
.bus_info = "Platform",
.version = VPFE_VERSION_CODE,
.capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING
};
static int sense_std(v4l2_std_id* std_id)
{
v4l2_std_id id = 0;
tvp5146_mode mode;
int ret;
ret = tvp5146_ctrl(TVP5146_GET_STD, &mode);
if(ret < 0)
return ret;
switch (mode & 0x7) {
case TVP5146_MODE_NTSC:
id = V4L2_STD_NTSC;
break;
case TVP5146_MODE_PAL:
id = V4L2_STD_PAL;
break;
case TVP5146_MODE_PAL_M:
id = V4L2_STD_PAL_M;
break;
case TVP5146_MODE_PAL_CN:
id = V4L2_STD_PAL_N;
break;
case TVP5146_MODE_SECAM:
id = V4L2_STD_SECAM;
break;
case TVP5146_MODE_PAL_60:
id = V4L2_STD_PAL_60;
break;
}
if (mode & 0x8) { /* square pixel mode */
id <<= 32;
}
if (mode == TVP5146_MODE_AUTO) {
id = VPFE_STD_AUTO; /* auto-detection for all other modes */
} else if (mode == TVP5146_MODE_AUTO_SQP) {
id = VPFE_STD_AUTO_SQP;
}
if(id == 0)
return -EINVAL;
*std_id = id;
return 0;
}
static irqreturn_t vpfe_isr(int irq, void *dev_id)
{
vpfe_obj *vpfe = &vpfe_device;
int fid;
/* check which field we are in hardware */
fid = ccdc_getfid();
vpfe->field_id ^= 1; /* switch the software maintained field id */
debug_print(KERN_INFO "field id = %x:%x.\n", fid, vpfe->field_id);
if (fid == vpfe->field_id) { /* we are in-sync here, continue */
if (fid == 0) {
/* One frame is just being captured. If the next frame
is available, release the current frame and move on */
if (vpfe->curFrm != vpfe->nextFrm) {
vpfe->curFrm->state = STATE_DONE;
wake_up_interruptible(&vpfe->curFrm->done);
vpfe->curFrm = vpfe->nextFrm;
}
/* based on whether the two fields are stored interleavely */
/* or separately in memory, reconfigure the CCDC memory address */
if (vpfe->field == V4L2_FIELD_SEQ_TB) {
u32 addr =
vpfe->curFrm->boff + vpfe->field_offset;
ccdc_setfbaddr((unsigned long)addr);
}
} else if (fid == 1) {
/* if one field is just being captured */
/* configure the next frame */
/* get the next frame from the empty queue */
/* if no frame is available, hold on to the current buffer */
if (!list_empty(&vpfe->dma_queue)
&& vpfe->curFrm == vpfe->nextFrm) {
vpfe->nextFrm = list_entry(vpfe->dma_queue.next,
struct videobuf_buffer, queue);
list_del(&vpfe->nextFrm->queue);
vpfe->nextFrm->state = STATE_ACTIVE;
ccdc_setfbaddr(
(unsigned long)vpfe->nextFrm->boff);
}
if (vpfe->mode_changed) {
ccdc_setwin(&vpfe->ccdc_params);
/* update the field offset */
vpfe->field_offset =
(vpfe->vwin.height - 2) * vpfe->vwin.width;
vpfe->mode_changed = FALSE;
}
}
} else if (fid == 0) {
/* recover from any hardware out-of-sync due to */
/* possible switch of video source */
/* for fid == 0, sync up the two fids */
/* for fid == 1, no action, one bad frame will */
/* go out, but it is not a big deal */
vpfe->field_id = fid;
}
debug_print(KERN_INFO "interrupt returned.\n");
return IRQ_RETVAL(1);
}
/* this is the callback function called from videobuf_qbuf() function */
/* the buffer is prepared and queued into the dma queue */
static int buffer_prepare(struct videobuf_queue *q,
struct videobuf_buffer *vb,
enum v4l2_field field)
{
vpfe_obj *vpfe = &vpfe_device;
if (vb->state == STATE_NEEDS_INIT) {
vb->width = vpfe->vwin.width;
vb->height = vpfe->vwin.height;
vb->size = VPFE_MAX_FBUF_SIZE;
vb->field = field;
}
vb->state = STATE_PREPARED;
return 0;
}
static void
buffer_config(struct videobuf_queue *q, unsigned int count)
{
vpfe_obj *vpfe = &vpfe_device;
int i;
for(i = 0; i < count; i++) {
q->bufs[i]->boff = virt_to_phys(vpfe->fbuffers[i]);
debug_print(KERN_INFO "buffer address: %x\n", q->bufs[i]->boff);
}
}
static int
buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
vpfe_obj *vpfe = &vpfe_device;
int i;
*size = VPFE_MAX_FBUF_SIZE;
for (i = VPFE_DEFNUM_FBUFS; i < *count; i++) {
u32 size = PAGE_SIZE << VPFE_MAX_FBUF_ORDER;
void *mem = (void *)__get_free_pages(GFP_KERNEL |GFP_DMA,
VPFE_MAX_FBUF_ORDER);
if (mem) {
unsigned long adr = (unsigned long)mem;
while (size > 0) {
/* make sure the frame buffers are never
swapped out of memory */
SetPageReserved(virt_to_page(adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
vpfe->fbuffers[i] = mem;
} else {
break;
}
}
*count = vpfe->numbuffers = i;
return 0;
}
static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
vpfe_obj *vpfe = &vpfe_device;
/* add the buffer to the DMA queue */
list_add_tail(&vb->queue, &vpfe->dma_queue);
vb->state = STATE_QUEUED;
}
static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
/* free the buffer if it is not one of the 3 allocated at initializaiton time */
if(vb->i < vpfe_device.numbuffers
&& vb->i >= VPFE_DEFNUM_FBUFS
&& vpfe_device.fbuffers[vb->i]){
free_pages((unsigned long)vpfe_device.fbuffers[vb->i],
VPFE_MAX_FBUF_ORDER);
vpfe_device.fbuffers[vb->i] = NULL;
}
}
static struct videobuf_queue_ops video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
.buf_config = buffer_config,
};
static int vpfe_doioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
vpfe_obj *vpfe = &vpfe_device;
vpfe_fh *fh = file->private_data;
int ret = 0;
switch (cmd) {
case VIDIOC_S_CTRL:
case VIDIOC_S_FMT:
case VIDIOC_S_STD:
case VIDIOC_S_CROP:
ret = v4l2_prio_check(&vpfe->prio, &fh->prio);
if (0 != ret) {
return ret;
}
break;
}
switch (cmd) {
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap =
(struct v4l2_capability *)arg;
memset(cap, 0, sizeof(*cap));
*cap = vpfe_drvcap;
break;
}
case VIDIOC_ENUM_FMT:
{
struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *)arg;
u32 index = fmt->index;
memset(fmt, 0, sizeof(*fmt));
fmt->index = index;
if (index == 0) {
/* only yuv4:2:2 format is supported at this point */
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
strcpy(fmt->description,
"YCbCr4:2:2 Interleaved UYUV");
fmt->pixelformat = V4L2_PIX_FMT_UYVY;
} else if (index == 1) {
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
strcpy(fmt->description,
"YCbCr4:2:2 Interleaved YUYV");
fmt->pixelformat = V4L2_PIX_FMT_YUYV;
} else {
ret = -EINVAL;
}
break;
}
case VIDIOC_G_FMT:
{
struct v4l2_format *fmt = (struct v4l2_format *)arg;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
ret = -EINVAL;
} else {
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
down_interruptible(&vpfe->lock);
pixfmt->width = vpfe->vwin.width;
pixfmt->height = vpfe->vwin.height;
pixfmt->field = vpfe->field;
pixfmt->pixelformat = vpfe->pixelfmt;
pixfmt->bytesperline = pixfmt->width * 2;
pixfmt->sizeimage =
pixfmt->bytesperline * pixfmt->height;
pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
up(&vpfe->lock);
}
break;
}
case VIDIOC_S_FMT:
{
struct v4l2_format *fmt = (struct v4l2_format *)arg;
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
ccdc_params_ycbcr *params = &vpfe->ccdc_params;
if (vpfe->started) { /* make sure streaming is not started */
ret = -EBUSY;
break;
}
down_interruptible(&vpfe->lock);
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
ret = -EINVAL;
up(&vpfe->lock);
break;
}
if ((pixfmt->width + vpfe->vwin.left <=
vpfe->bounds.width)
& (pixfmt->height + vpfe->vwin.top <=
vpfe->bounds.height)) {
/* this is the case when no scaling is supported */
/* crop window is directed modified */
vpfe->vwin.height = pixfmt->height;
vpfe->vwin.width = pixfmt->width;
params->win.width = pixfmt->width;
params->win.height = pixfmt->height;
} else {
ret = -EINVAL;
up(&vpfe->lock);
break;
}
/* setup the CCDC parameters accordingly */
if (pixfmt->pixelformat == V4L2_PIX_FMT_YUYV) {
params->pix_order = CCDC_PIXORDER_YCBYCR;
vpfe->pixelfmt = pixfmt->pixelformat;
} else if (pixfmt->pixelformat == V4L2_PIX_FMT_UYVY) {
params->pix_order = CCDC_PIXORDER_CBYCRY;
vpfe->pixelfmt = pixfmt->pixelformat;
} else {
ret = -EINVAL; /* not supported format */
up(&vpfe->lock);
break;
}
if (pixfmt->field == V4L2_FIELD_NONE
|| pixfmt->field == V4L2_FIELD_INTERLACED) {
params->buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED;
vpfe->field = pixfmt->field;
} else if (pixfmt->field == V4L2_FIELD_SEQ_TB) {
params->buf_type = CCDC_BUFTYPE_FLD_SEPARATED;
vpfe->field = pixfmt->field;
} else {
ret = -EINVAL;
}
up(&vpfe->lock);
break;
}
case VIDIOC_TRY_FMT:
{
struct v4l2_format *fmt = (struct v4l2_format *)arg;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
ret = -EINVAL;
} else {
struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
if (pixfmt->width > vpfe->bounds.width
|| pixfmt->height > vpfe->bounds.height
|| (pixfmt->pixelformat != V4L2_PIX_FMT_UYVY
&& pixfmt->pixelformat !=
V4L2_PIX_FMT_YUYV)) {
ret = -EINVAL;
}
}
break;
}
case VIDIOC_G_STD:
{
v4l2_std_id *id = (v4l2_std_id *) arg;
*id = vpfe->std;
break;
}
case VIDIOC_S_STD:
{
v4l2_std_id id = *(v4l2_std_id *) arg;
tvp5146_mode mode = TVP5146_MODE_INV;
int sqp = 0;
if (vpfe->started) { /* make sure streaming is not started */
ret = -EBUSY;
break;
}
down_interruptible(&vpfe->lock);
if (id & V4L2_STD_625_50) {
vpfe->std = id;
vpfe->bounds = vpfe->vwin = pal_bounds;
vpfe->pixelaspect = pal_aspect;
vpfe->ccdc_params.win = pal_bounds;
} else if (id & V4L2_STD_525_60) {
vpfe->std = id;
vpfe->bounds = vpfe->vwin = ntsc_bounds;
vpfe->pixelaspect = ntsc_aspect;
vpfe->ccdc_params.win = ntsc_bounds;
} else if (id & VPFE_STD_625_50_SQP) {
vpfe->std = id;
vpfe->bounds = vpfe->vwin = palsp_bounds;
vpfe->pixelaspect = sp_aspect;
sqp = 1;
id >>= 32;
} else if (id & VPFE_STD_525_60_SQP) {
vpfe->std = id;
sqp = 1;
vpfe->std = id;
id >>= 32;
vpfe->bounds = vpfe->vwin = ntscsp_bounds;
vpfe->pixelaspect = sp_aspect;
vpfe->ccdc_params.win = ntscsp_bounds;
} else if (id & VPFE_STD_AUTO) {
mode = TVP5146_MODE_AUTO;
vpfe->bounds = vpfe->vwin = pal_bounds;
vpfe->pixelaspect = pal_aspect;
vpfe->ccdc_params.win = pal_bounds;
vpfe->std = id;
} else if (id & VPFE_STD_AUTO_SQP) {
vpfe->std = id;
vpfe->bounds = vpfe->vwin = palsp_bounds;
vpfe->pixelaspect = sp_aspect;
sqp = 1;
mode = TVP5146_MODE_AUTO_SQP;
vpfe->pixelaspect = sp_aspect;
} else {
ret = -EINVAL;
}
if (id == V4L2_STD_PAL_60) {
mode = TVP5146_MODE_PAL_60;
} else if (id == V4L2_STD_PAL_M) {
mode = TVP5146_MODE_PAL_M;
} else if (id == V4L2_STD_PAL_Nc
|| id == V4L2_STD_PAL_N) {
mode = TVP5146_MODE_PAL_CN;
} else if (id & V4L2_STD_PAL) {
mode = TVP5146_MODE_PAL;
} else if (id & V4L2_STD_NTSC) {
mode = TVP5146_MODE_NTSC;
} else if (id & V4L2_STD_SECAM) {
mode = TVP5146_MODE_SECAM;
}
vpfe->tvp5146_params.mode = mode | (sqp << 3);
tvp5146_ctrl(TVP5146_CONFIG, &vpfe->tvp5146_params);
up(&vpfe->lock);
break;
}
case VIDIOC_ENUMSTD:
{
struct v4l2_standard *std = (struct v4l2_standard *)arg;
u32 index = std->index;
memset(std, 0, sizeof(*std));
std->index = index;
if (index == 0) {
std->id = V4L2_STD_525_60;
strcpy(std->name, "SD-525line-30fps");
std->framelines = 525;
std->frameperiod.numerator = 1001;
std->frameperiod.denominator = 30000;
} else if (index == 1) {
std->id = V4L2_STD_625_50;
strcpy(std->name, "SD-625line-25fps");
std->framelines = 625;
std->frameperiod.numerator = 1;
std->frameperiod.denominator = 25;
} else if (index == 2) {
std->id = VPFE_STD_625_50_SQP;
strcpy(std->name,
"SD-625line-25fps square pixel");
std->framelines = 625;
std->frameperiod.numerator = 1;
std->frameperiod.denominator = 25;
} else if (index == 3) {
std->id = VPFE_STD_525_60_SQP;
strcpy(std->name,
"SD-525line-25fps square pixel");
std->framelines = 525;
std->frameperiod.numerator = 1001;
std->frameperiod.denominator = 30000;
} else if (index == 4) {
std->id = VPFE_STD_AUTO;
strcpy(std->name, "automatic detect");
std->framelines = 625;
std->frameperiod.numerator = 1;
std->frameperiod.denominator = 1;
} else if (index == 5) {
std->id = VPFE_STD_AUTO_SQP;
strcpy(std->name,
"automatic detect square pixel");
std->framelines = 625;
std->frameperiod.numerator = 1;
std->frameperiod.denominator = 1;
} else {
ret = -EINVAL;
}
break;
}
case VIDIOC_ENUMINPUT:
{
u32 index=0;
struct v4l2_input *input = (struct v4l2_input *)arg;
if (input->index > 1) /* only two inputs are available */
ret = -EINVAL;
index = input->index;
memset(input, 0, sizeof(*input));
input->index = index;
input->type = V4L2_INPUT_TYPE_CAMERA;
input->std = V4L2_STD_ALL;
if(input->index == 0){
sprintf(input->name, "COMPOSITE");
}else if(input->index == 1) {
sprintf(input->name, "S-VIDEO");
}
break;
}
case VIDIOC_G_INPUT:
{
int *index = (int *)arg;
*index = vpfe->tvp5146_params.amuxmode;
break;
}
case VIDIOC_S_INPUT:
{
int *index = (int *)arg;
if (*index > 1 || *index < 0) {
ret = -EINVAL;
}
vpfe->tvp5146_params.amuxmode = *index;
tvp5146_ctrl(TVP5146_SET_AMUXMODE, index);
break;
}
case VIDIOC_CROPCAP:
{
struct v4l2_cropcap *cropcap =
(struct v4l2_cropcap *)arg;
cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
down_interruptible(&vpfe->lock);
cropcap->bounds = cropcap->defrect = vpfe->vwin;
cropcap->pixelaspect = vpfe->pixelaspect;
up(&vpfe->lock);
break;
}
case VIDIOC_G_PARM:
{
struct v4l2_streamparm *parm =
(struct v4l2_streamparm *)arg;
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
/* only capture is supported */
ret = -EINVAL;
} else {
struct v4l2_captureparm *capparm =
&parm->parm.capture;
memset(capparm, 0,
sizeof(struct v4l2_captureparm));
down_interruptible(&vpfe->lock);
if (vpfe->std & V4L2_STD_625_50) {
capparm->timeperframe.numerator = 1;
capparm->timeperframe.denominator = 25; /* PAL 25fps */
} else {
capparm->timeperframe.numerator = 1001;
capparm->timeperframe.denominator = 30000; /*NTSC 29.97fps */
}
capparm->readbuffers = vpfe->numbuffers;
up(&vpfe->lock);
}
break;
}
case VIDIOC_G_CTRL:
down_interruptible(&vpfe->lock);
tvp5146_ctrl(VIDIOC_G_CTRL, arg);
up(&vpfe->lock);
break;
case VIDIOC_S_CTRL:
down_interruptible(&vpfe->lock);
tvp5146_ctrl(VIDIOC_S_CTRL, arg);
up(&vpfe->lock);
break;
case VIDIOC_QUERYCTRL:
down_interruptible(&vpfe->lock);
tvp5146_ctrl(VIDIOC_QUERYCTRL, arg);
up(&vpfe->lock);
break;
case VIDIOC_G_CROP:
{
struct v4l2_crop *crop = arg;
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
ret = -EINVAL;
} else {
crop->c = vpfe->vwin;
}
break;
}
case VIDIOC_S_CROP:
{
struct v4l2_crop *crop = arg;
ccdc_params_ycbcr *params = &vpfe->ccdc_params;
if (vpfe->started) { /* make sure streaming is not started */
ret = -EBUSY;
break;
}
/*adjust the width to 16 pixel boundry */
crop->c.width = ((crop->c.width + 15 )/16 ) * 16;
/* make sure parameters are valid */
if (crop->type == V4L2_BUF_TYPE_VIDEO_CAPTURE
&& (crop->c.left + crop->c.width
<= vpfe->bounds.left + vpfe->bounds.width)
&& (crop->c.top + crop->c.height
<= vpfe->bounds.top + vpfe->bounds.height)) {
down_interruptible(&vpfe->lock);
vpfe->vwin = crop->c;
params->win = vpfe->vwin;
up(&vpfe->lock);
} else {
ret = -EINVAL;
}
break;
}
case VIDIOC_QUERYSTD:
{
v4l2_std_id *id = (v4l2_std_id *) arg;
down_interruptible(&vpfe->lock);
ret = sense_std(id);
up(&vpfe->lock);
break;
}
case VIDIOC_G_PRIORITY:
{
enum v4l2_priority *p = arg;
*p = v4l2_prio_max(&vpfe->prio);
break;
}
case VIDIOC_S_PRIORITY:
{
enum v4l2_priority *p = arg;
ret = v4l2_prio_change(&vpfe->prio, &fh->prio, *p);
break;
}
case VIDIOC_REQBUFS:
if (vpfe->io_usrs != 0) {
ret = -EBUSY;
break;
}
down_interruptible(&vpfe->lock);
videobuf_queue_init(&vpfe->bufqueue, &video_qops, NULL,
&vpfe->irqlock, V4L2_BUF_TYPE_VIDEO_CAPTURE, vpfe->field,
sizeof(struct videobuf_buffer), fh);
videobuf_set_buftype(&vpfe->bufqueue, VIDEOBUF_BUF_LINEAR);
fh->io_allowed = TRUE;
vpfe->io_usrs = 1;
INIT_LIST_HEAD(&vpfe->dma_queue);
ret = videobuf_reqbufs(&vpfe->bufqueue, arg);
up(&vpfe->lock);
break;
case VIDIOC_QUERYBUF:
ret = videobuf_querybuf(&vpfe->bufqueue, arg);
break;
case VIDIOC_QBUF:
if (!fh->io_allowed)
ret = -EACCES;
else
ret = videobuf_qbuf(&vpfe->bufqueue, arg);
break;
case VIDIOC_DQBUF:
if (!fh->io_allowed)
ret = -EACCES;
else
ret = videobuf_dqbuf(&vpfe->bufqueue, arg, 0);
break;
case VIDIOC_STREAMON:
if (!fh->io_allowed) {
ret = -EACCES;
break;
}
if(vpfe->started){
ret = -EBUSY;
break;
}
ret = videobuf_streamon(&vpfe->bufqueue);
if(ret) break;
down_interruptible(&vpfe->lock);
/* get the current and next frame buffers */
/* we expect at least one buffer is in driver at this point */
/* if not, error is returned */
if (list_empty(&vpfe->dma_queue)) {
ret = -EIO;
break;
}
debug_print(KERN_INFO "cur frame %x.\n",
vpfe->dma_queue.next);
vpfe->nextFrm = vpfe->curFrm =
list_entry(vpfe->dma_queue.next,
struct videobuf_buffer, queue);
/* remove the buffer from the queue */
list_del(&vpfe->curFrm->queue);
vpfe->curFrm->state = STATE_ACTIVE;
/* sense the current video input standard */
tvp5146_ctrl(TVP5146_CONFIG, &vpfe->tvp5146_params);
/* configure the ccdc and resizer as needed */
/* start capture by enabling CCDC and resizer */
ccdc_config_ycbcr(&vpfe->ccdc_params);
/* setup the memory address for the frame buffer */
ccdc_setfbaddr(((unsigned long)(vpfe->curFrm->boff)));
/* enable CCDC */
vpfe->field_id = 0;
vpfe->started = TRUE;
vpfe->mode_changed = FALSE;
vpfe->field_offset =
(vpfe->vwin.height - 2) * vpfe->vwin.width;
ccdc_enable(TRUE);
up(&vpfe->lock);
debug_print(KERN_INFO "started video streaming.\n");
break;
case VIDIOC_STREAMOFF:
{
if (!fh->io_allowed) {
ret = -EACCES;
break;
}
if(!vpfe->started){
ret = -EINVAL;
break;
}
/* disable CCDC */
down_interruptible(&vpfe->lock);
ccdc_enable(FALSE);
vpfe->started = FALSE;
up(&vpfe->lock);
ret = videobuf_streamoff(&vpfe->bufqueue);
break;
}
case VPFE_CMD_CONFIG_CCDC:
{
/* this can be used directly and bypass the V4L2 APIs */
ccdc_params_ycbcr *params = &vpfe->ccdc_params;
if(vpfe->started){
/* only allowed if streaming is not started */
ret = -EBUSY;
break;
}
down_interruptible(&vpfe->lock);
/* make sure the other v4l2 related fields
have consistant settings */
*params = (*(ccdc_params_ycbcr *) arg);
vpfe->vwin = params->win;
if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) {
vpfe->field = V4L2_FIELD_INTERLACED;
} else if (params->buf_type ==
CCDC_BUFTYPE_FLD_SEPARATED) {
vpfe->field = V4L2_FIELD_SEQ_TB;
}
if (params->pix_order == CCDC_PIXORDER_YCBYCR) {
vpfe->pixelfmt = V4L2_PIX_FMT_YUYV;
} else if (params->pix_order == CCDC_PIXORDER_CBYCRY) {
vpfe->pixelfmt = V4L2_PIX_FMT_UYVY;
}
up(&vpfe->lock);
break;
}
case VPFE_CMD_CONFIG_TVP5146:
/* this can be used directly and bypass the V4L2 APIs */
{
/* the settings here must be consistant with that of the CCDC's,
driver does not check the consistancy */
tvp5146_params *params = (tvp5146_params *) arg;
v4l2_std_id std = 0;
if(vpfe->started){
/* only allowed if streaming is not started */
ret = -EBUSY;
break;
}
down_interruptible(&vpfe->lock);
/*make sure the other v4l2 related fields have consistant settings */
switch (params->mode & 0x7) {
case TVP5146_MODE_NTSC:
std = V4L2_STD_NTSC;
break;
case TVP5146_MODE_PAL:
std = V4L2_STD_PAL;
break;
case TVP5146_MODE_PAL_M:
std = V4L2_STD_PAL_M;
break;
case TVP5146_MODE_PAL_CN:
std = V4L2_STD_PAL_N;
break;
case TVP5146_MODE_SECAM:
std = V4L2_STD_SECAM;
break;
case TVP5146_MODE_PAL_60:
std = V4L2_STD_PAL_60;
break;
}
if (params->mode & 0x8) { /* square pixel mode */
std <<= 32;
}
if (params->mode == TVP5146_MODE_AUTO) { /* auto-detection modes */
std = VPFE_STD_AUTO;
} else if (params->mode == TVP5146_MODE_AUTO_SQP) {
std = VPFE_STD_AUTO_SQP;
}
if (std & V4L2_STD_625_50) {
vpfe->bounds = pal_bounds;
vpfe->pixelaspect = pal_aspect;
} else if (std & V4L2_STD_525_60) {
vpfe->bounds = ntsc_bounds;
vpfe->pixelaspect = ntsc_aspect;
} else if (std & VPFE_STD_625_50_SQP) {
vpfe->bounds = palsp_bounds;
vpfe->pixelaspect = sp_aspect;
} else if (std & VPFE_STD_525_60_SQP) {
vpfe->bounds = ntscsp_bounds;
vpfe->pixelaspect = sp_aspect;
}
vpfe->std = std;
tvp5146_ctrl(TVP5146_CONFIG, params);
vpfe->tvp5146_params = *params;
up(&vpfe->lock);
break;
}
default:
ret = -ENOIOCTLCMD;
break;
} /* end switch(cmd) */
return ret;
}
static int vpfe_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret;
ret = video_usercopy(inode, file, cmd, arg, vpfe_doioctl);
if( cmd == VIDIOC_S_FMT || cmd == VIDIOC_TRY_FMT ){
ret = video_usercopy(inode, file, VIDIOC_G_FMT,
arg, vpfe_doioctl);
}
return ret;
}
static int vpfe_mmap(struct file *file, struct vm_area_struct *vma)
{
return videobuf_mmap_mapper(&vpfe_device.bufqueue, vma);
}
static int vpfe_open(struct inode *inode, struct file *filep)
{
int minor = iminor(inode);
vpfe_obj *vpfe = NULL;
vpfe_fh *fh = NULL;
debug_print(KERN_INFO "vpfe: open minor=%d\n", minor);
/* check to make sure the minor numbers match */
if (vpfe_device.video_dev && vpfe_device.video_dev->minor == minor) {
vpfe = &vpfe_device;
} else { /* device not found here */
return -ENODEV;
}
/* allocate per filehandle data */
if ((fh = kmalloc(sizeof(*fh), GFP_KERNEL)) == NULL) {
return -ENOMEM;
}
filep->private_data = fh;
fh->dev = vpfe;
fh->io_allowed = FALSE;
fh->prio = V4L2_PRIORITY_UNSET;
v4l2_prio_open(&vpfe->prio, &fh->prio);
vpfe->usrs++;
return 0;
}
static int vpfe_release(struct inode *inode, struct file *filep)
{
vpfe_fh *fh = filep->private_data;
vpfe_obj *vpfe = fh->dev;
down_interruptible(&vpfe->lock);
if (fh->io_allowed) {
vpfe->io_usrs = 0;
ccdc_enable(FALSE);
vpfe->started = FALSE;
videobuf_queue_cancel(&vpfe->bufqueue);
vpfe->numbuffers = VPFE_DEFNUM_FBUFS;
}
vpfe->usrs--;
v4l2_prio_close(&vpfe->prio, &fh->prio);
filep->private_data = NULL;
kfree(fh);
up(&vpfe->lock);
return 0;
}
static struct file_operations vpfe_fops = {
.owner = THIS_MODULE,
.open = vpfe_open,
.release = vpfe_release,
.ioctl = vpfe_ioctl,
.mmap = vpfe_mmap
};
static struct video_device vpfe_video_template = {
.name = "vpfe",
.type = VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES,
.hardware = 0,
.fops = &vpfe_fops,
.minor = -1,
};
static void vpfe_platform_release(struct device *device)
{
/* This is called when the reference count goes to zero. */
}
static int __init vpfe_probe(struct device *device)
{
struct video_device *vfd;
vpfe_obj *vpfe = &vpfe_device;
/* alloc video device */
if ((vfd = video_device_alloc()) == NULL) {
return -ENOMEM;
}
*vfd = vpfe_video_template;
vfd->dev = device;
vfd->release = video_device_release;
snprintf(vfd->name, sizeof(vfd->name), "DM644X_VPFE_DRIVER_V%d.%d.%d",
(VPFE_VERSION_CODE >> 16) & 0xff,
(VPFE_VERSION_CODE >> 8) & 0xff, (VPFE_VERSION_CODE) & 0xff);
vpfe->video_dev = vfd;
vpfe->usrs = 0;
vpfe->io_usrs = 0;
vpfe->started = FALSE;
vpfe->latest_only = TRUE;
v4l2_prio_init(&vpfe->prio);
init_MUTEX(&vpfe->lock);
/* register video device */
debug_print(KERN_INFO "trying to register vpfe device.\n");
debug_print(KERN_INFO "vpfe=%x,vpfe->video_dev=%x\n", (int)vpfe,
(int)&vpfe->video_dev);
if (video_register_device(vpfe->video_dev, VFL_TYPE_GRABBER, -1) < 0) {
video_device_release(vpfe->video_dev);
vpfe->video_dev = NULL;
return -1;
}
debug_print(KERN_INFO "DM644X vpfe: driver version V%d.%d.%d loaded\n",
(VPFE_VERSION_CODE >> 16) & 0xff,
(VPFE_VERSION_CODE >> 8) & 0xff,
(VPFE_VERSION_CODE) & 0xff);
debug_print(KERN_INFO "vpfe: registered device video%d\n",
vpfe->video_dev->minor & 0x1f);
/* all done */
return 0;
}
static int vpfe_remove(struct device *device)
{
/* un-register device */
video_unregister_device(vpfe_device.video_dev);
return 0;
}
#ifdef NEW
static struct platform_driver vpfe_driver = {
.driver = {
.name = "VPFE",
.owner = THIS_MODULE,
},
.probe = vpfe_probe,
.remove = vpfe_remove,
};
#else
static struct device_driver vpfe_driver = {
.name = "vpfe",
.bus = &platform_bus_type,
.probe = vpfe_probe,
.remove = vpfe_remove,
};
#endif
static struct platform_device _vpfe_device = {
.name = "vpfe",
.id = 1,
.dev = {
.release = vpfe_platform_release,
}
};
static int vpfe_init(void)
{
int i = 0;
void *mem;
/* allocate memory at initialization time to guarentee availability */
for (i = 0; i < VPFE_DEFNUM_FBUFS; i++) {
mem = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA,
VPFE_MAX_FBUF_ORDER);
if (mem) {
unsigned long adr = (unsigned long)mem;
u32 size = PAGE_SIZE << VPFE_MAX_FBUF_ORDER;
while (size > 0) {
/* make sure the frame buffers
are never swapped out of memory */
SetPageReserved(virt_to_page(adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
vpfe_device.fbuffers[i] = (u8 *) mem;
debug_print(KERN_INFO "memory address %d\t%x\n", i,
mem);
} else {
while (--i >= 0) {
free_pages((unsigned long)vpfe_device.fbuffers[i],
VPFE_MAX_FBUF_ORDER);
}
debug_print(KERN_INFO
"frame buffer memory allocation failed.\n");
return -ENOMEM;
}
}
if (driver_register(&vpfe_driver) != 0) {
debug_print(KERN_INFO "driver registration failed\n");
return -1;
}
if (platform_device_register(&_vpfe_device) != 0) {
driver_unregister(&vpfe_driver);
debug_print(KERN_INFO "device registration failed\n");
return -1;
}
ccdc_reset();
tvp5146_ctrl(TVP5146_RESET, NULL);
/* configure the tvp5146 to default parameters */
tvp5146_ctrl(TVP5146_CONFIG, &vpfe_device.tvp5146_params);
/* setup interrupt handling */
request_irq(IRQ_VDINT0, vpfe_isr, SA_INTERRUPT,
"dm644xv4l2", (void *)&vpfe_device);
printk(KERN_INFO "DaVinci v4l2 capture driver V1.0 loaded\n");
return 0;
}
static void vpfe_cleanup(void)
{
int i = vpfe_device.numbuffers;
platform_device_unregister(&_vpfe_device);
driver_unregister(&vpfe_driver);
/* disable interrupt */
free_irq(IRQ_VDINT0, &vpfe_device);
while (--i >= 0) {
free_pages((unsigned long)vpfe_device.fbuffers[i],
VPFE_MAX_FBUF_ORDER);
}
debug_print(KERN_INFO "vpfe: un-registered device video.\n");
}
module_init(vpfe_init);
module_exit(vpfe_cleanup);
/*
*
*
* Copyright (C) 2006 Texas Instruments Inc
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* tvp5146.c */
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/videodev.h>
#include <media/tvp5146.h>
#define debug_print(x...) //printk(x)
static struct i2c_client tvp5146_i2c_client;
static struct i2c_driver tvp5146_i2c_driver;
static int i2c_read_reg(struct i2c_client *client, u8 reg, u8 * val);
static int i2c_write_reg(struct i2c_client *client, u8 reg, u8 val);
static int configtvp5146(void *arg);
static int clrtvp5146lostlock(void);
static int enabletvp5146agc(int arg);
static int getctrl(void *arg);
static int gettvp5146status(void *arg);
static int powerdowntvp5146(int powerdownenable);
static int queryctrl(void *arg);
static int resettvp5146(void);
static int setctrl(void *arg);
static int settvp5146amuxmode(int mode);
static int settvp5146brightness(int arg);
static int settvp5146contrast(int arg);
static int settvp5146hue(int arg);
static int settvp5146saturation(int arg);
static int settvp5146std(int arg);
static int setup656sync(int enable);
/*
* ======== configtvp5146 ========
*/
static int configtvp5146(void *arg)
{
tvp5146_params *tvp5146params = (tvp5146_params *) arg;
int ret = 0;
ret |= setup656sync(tvp5146params->enablebt656sync);
ret |= settvp5146amuxmode(tvp5146params->amuxmode);
ret |= settvp5146std(tvp5146params->mode);
return ret;
}
/*
* ======== clrtvp5146lostlock ========
*/
static int clrtvp5146lostlock(void)
{
int ret = 0;
u8 clr = 1;
ret = i2c_write_reg(&tvp5146_i2c_client, 0x39, clr);
return ret;
}
/*
* ======== enabletvp5146agc ========
*/
static int enabletvp5146agc(int arg)
{
int ret = 0;
int agc;
if (arg == TRUE) {
agc = 0xF;
} else {
agc = 0xC;
}
ret = i2c_write_reg(&tvp5146_i2c_client, 0x01, agc);
return ret;
}
/*
* ======== gettvpctrl ========
*/
static int getctrl(void *arg)
{
struct v4l2_control *ctrl = arg;
int ret = 0;
u8 value;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ret = i2c_read_reg(&tvp5146_i2c_client, 0x09, &value);
ctrl->value = value;
break;
case V4L2_CID_CONTRAST:
ret = i2c_read_reg(&tvp5146_i2c_client, 0x0A, &value);
ctrl->value = value;
break;
case V4L2_CID_SATURATION:
ret = i2c_read_reg(&tvp5146_i2c_client, 0x0B, &value);
ctrl->value = value;
break;
case V4L2_CID_HUE:
ret = i2c_read_reg(&tvp5146_i2c_client, 0x0C, &value);
ctrl->value = value;
break;
case V4L2_CID_AUTOGAIN:
ret = i2c_read_reg(&tvp5146_i2c_client, 0x01, &value);
if ((value & 0x3) == 3) {
ctrl->value = TRUE;
} else {
ctrl->value = FALSE;
}
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/*
* ======== gettvp5146std ========
*/
static int gettvp5146std(tvp5146_mode * mode)
{
int ret = 0;
u8 output1;
u8 std;
u8 lock_status;
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x2, &std);
std &= 0x7;
if(std == TVP5146_MODE_AUTO){
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x3F, &std);
}
std &= 0x7;
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x33, &output1);
*mode = std | ((output1 & 0x80) >> 4); /* square pixel status */
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x3A, &lock_status);
if ((lock_status & 0xe) != 0xe) {
/* not quite locked */
ret = -EAGAIN;
}
return ret;
}
/*
* ======== gettvp5146status ========
*/
static int gettvp5146status(void *arg)
{
int ret = 0;
tvp5146_status *status = (tvp5146_status *) arg;
u8 agc, brightness, contrast, hue, saturation;
u8 status_byte;
u8 std;
u8 output1;
ret = i2c_read_reg(&tvp5146_i2c_client, 0x01, &agc);
if ((agc & 0x3) == 3) {
status->agc_enable = TRUE;
} else {
status->agc_enable = FALSE;
}
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x09, &brightness);
status->brightness = brightness;
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x0A, &contrast);
status->contrast = contrast;
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x0B, &saturation);
status->saturation = saturation;
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x0C, &hue);
status->hue = hue;
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x3A, &status_byte);
status->field_rate = (status_byte & 0x20) ? 50 : 60;
status->lost_lock = (status_byte & 0x10) >> 4;
status->csubc_lock = (status_byte & 0x8) >> 3;
status->v_lock = (status_byte & 0x4) >> 2;
status->h_lock = (status_byte & 0x2) >> 1;
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x3F, &std);
ret |= i2c_read_reg(&tvp5146_i2c_client, 0x33, &output1);
if (std | 0x80) { /* auto switch mode */
status->video_std = TVP5146_MODE_AUTO;
} else {
status->video_std = std;
}
status->video_std |= ((output1 & 0x80) >> 4); /* square pixel status */
return ret;
}
/*
* ======== powerdowntvp5146 ========
*/
static int powerdowntvp5146(int powerdownenable)
{
u8 powerdownsettings = 0x01;
/*Put _tvp5146 in power down mode */
if (!powerdownenable) {
powerdownsettings = 0x00;
}
return i2c_write_reg(&tvp5146_i2c_client, 0x03, powerdownsettings);
}
/*
* ======== resettvp5146========
*/
static int resettvp5146(void)
{
setup656sync(TRUE);
settvp5146amuxmode(TVP5146_AMUX_COMPOSITE);
return powerdowntvp5146(FALSE);
}
/*
* ======== queryctrl ========
*/
static int queryctrl(void *arg)
{
struct v4l2_queryctrl *queryctrl = arg;
int ret = 0;
int id = queryctrl->id;
memset(queryctrl, 0, sizeof(*queryctrl));
queryctrl->id = id;
switch (id) {
case V4L2_CID_BRIGHTNESS:
strcpy(queryctrl->name, "BRIGHTNESS");
queryctrl->type = V4L2_CTRL_TYPE_INTEGER;
queryctrl->minimum = 0;
queryctrl->maximum = 255;
queryctrl->step = 1;
queryctrl->default_value = 128;
break;
case V4L2_CID_CONTRAST:
strcpy(queryctrl->name, "CONTRAST");
queryctrl->type = V4L2_CTRL_TYPE_INTEGER;
queryctrl->minimum = 0;
queryctrl->maximum = 255;
queryctrl->step = 1;
queryctrl->default_value = 128;
break;
case V4L2_CID_SATURATION:
strcpy(queryctrl->name, "SATURATION");
queryctrl->type = V4L2_CTRL_TYPE_INTEGER;
queryctrl->minimum = 0;
queryctrl->maximum = 255;
queryctrl->step = 1;
queryctrl->default_value = 128;
break;
case V4L2_CID_HUE:
strcpy(queryctrl->name, "HUE");
queryctrl->type = V4L2_CTRL_TYPE_INTEGER;
queryctrl->minimum = -128; /* -180 DEGREE */
queryctrl->maximum = 127; /* 180 DEGREE */
queryctrl->step = 1;
queryctrl->default_value = 0; /* 0 DEGREE */
break;
case V4L2_CID_AUTOGAIN:
strcpy(queryctrl->name, "Automatic Gain Control");
queryctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
queryctrl->minimum = 0;
queryctrl->maximum = 1;
queryctrl->step = 1;
queryctrl->default_value = 1;
break;
default:
if (id < V4L2_CID_LASTP1)
queryctrl->flags = V4L2_CTRL_FLAG_DISABLED;
else
ret = -EINVAL;
break;
} /* end switch (id) */
return ret;
}
/*
* ======== setctrl ========
*/
static int setctrl(void *arg)
{
struct v4l2_control *ctrl = arg;
int ret = 0;
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ret = settvp5146brightness(ctrl->value);
break;
case V4L2_CID_CONTRAST:
ret = settvp5146contrast(ctrl->value);
break;
case V4L2_CID_SATURATION:
ret = settvp5146saturation(ctrl->value);
break;
case V4L2_CID_HUE:
ret = settvp5146hue(ctrl->value);
break;
case V4L2_CID_AUTOGAIN:
ret = enabletvp5146agc(ctrl->value);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
/*
* ======== settvp5146amuxmode ========
*/
static int settvp5146amuxmode(int arg)
{
u8 input_sel;
if (arg == TVP5146_AMUX_COMPOSITE) { /* composite */
input_sel = 0x05;
} else if (arg == TVP5146_AMUX_SVIDEO) { /* s-video */
input_sel = 0x46;
} else {
return -EINVAL;
}
return i2c_write_reg(&tvp5146_i2c_client, 0x00, input_sel);
}
/*
* ======== settvp5146brightness ========
*/
static int settvp5146brightness(int arg)
{
int ret = 0;
u8 brightness = (u8) arg;
ret = i2c_write_reg(&tvp5146_i2c_client, 0x09, brightness);
return ret;
}
/*
* ======== settvp5146contrast ========
*/
static int settvp5146contrast(int arg)
{
int ret = 0;
u8 contrast = (u8) arg;
ret = i2c_write_reg(&tvp5146_i2c_client, 0x0A, contrast);
return ret;
}
/*
* ======== settvp5146hue ========
*/
static int settvp5146hue(int arg)
{
int ret = 0;
u8 hue = (u8) arg;
ret = i2c_write_reg(&tvp5146_i2c_client, 0x0C, hue);
return ret;
}
static int settvp5146saturation(int arg)
{
int ret = 0;
u8 saturation = (u8) arg;
ret = i2c_write_reg(&tvp5146_i2c_client, 0x0B, saturation);
return ret;
}
static int settvp5146std(int arg)
{
int ret = 0;
u8 std = (u8) arg & 0x7; /* the 4th-bit is for squre pixel sampling */
u8 output1;
/* setup the sampling rate: 601 or square pixel */
debug_print(KERN_INFO "reading i2c registers.\n");
ret = i2c_read_reg(&tvp5146_i2c_client, 0x33, &output1);
output1 |= ((arg & 0x8) << 4);
ret = i2c_write_reg(&tvp5146_i2c_client, 0x33, output1);
/* setup the video standard */
ret |= i2c_write_reg(&tvp5146_i2c_client, 0x02, std);
/* if autoswitch mode, enable all modes for autoswitch */
if (std == TVP5146_MODE_AUTO) {
u8 mask = 0x3F; /* enable autoswitch for all standards */
ret = i2c_write_reg(&tvp5146_i2c_client, 0x04, mask);
}
return ret;
}
/*
* ======== setup656sync ========
*/
static int setup656sync(int enable)
{
int output1, output2, output3, output4;
int output5, output6;
int ret = 0;
if (enable) {
output1 = 0x40;
output4 = 0xFF;
output6 = 0;
} else {
output1 = 0x43;
output4 = 0xAF;
output6 = 0x1E;
}
output2 = 0x11; /* enable clock, enable Y[9:0] */
output3 = 0x0;
output5 = 0x4;
ret |= i2c_write_reg(&tvp5146_i2c_client, 0x33, output1);
ret |= i2c_write_reg(&tvp5146_i2c_client, 0x34, output2);
ret |= i2c_write_reg(&tvp5146_i2c_client, 0x36, output4);
ret |= i2c_write_reg(&tvp5146_i2c_client, 0x08, output3);
ret |= i2c_write_reg(&tvp5146_i2c_client, 0x0e, output5);
ret |= i2c_write_reg(&tvp5146_i2c_client, 0x32, output6);
return ret;
}
/*
* ======== tvp5146_ctrl ========
*/
int tvp5146_ctrl(tvp5146_cmd cmd, void *arg)
{
int ret = 0;
switch (cmd) {
case TVP5146_CONFIG:
ret = configtvp5146(arg);
break;
case TVP5146_RESET:
ret = resettvp5146();
break;
case TVP5146_POWERDOWN:
ret = powerdowntvp5146(*(int *)arg);
break;
case TVP5146_SET_AMUXMODE:
ret = settvp5146amuxmode(*(int *)arg);
break;
case TVP5146_SET_BRIGHTNESS:
ret = settvp5146brightness(*(int *)arg);
break;
case TVP5146_SET_CONTRAST:
ret = settvp5146contrast(*(int *)arg);
break;
case TVP5146_SET_HUE:
ret = settvp5146hue(*(int *)arg);
break;
case TVP5146_SET_SATURATION:
ret = settvp5146saturation(*(int *)arg);
break;
case TVP5146_SET_AGC:
ret = enabletvp5146agc(*(int *)arg);
break;
case TVP5146_SET_VIDEOSTD:
ret = settvp5146std(*(int *)arg);
break;
case TVP5146_CLR_LOSTLOCK:
ret = clrtvp5146lostlock();
break;
case TVP5146_GET_STATUS:
ret = gettvp5146status(arg);
break;
case TVP5146_GET_STD:
ret = gettvp5146std(arg);
break;
case VIDIOC_QUERYCTRL:
ret = queryctrl(arg);
break;
case VIDIOC_G_CTRL:
ret = getctrl(arg);
break;
case VIDIOC_S_CTRL:
ret = setctrl(arg);
break;
default:
ret = -EINVAL;
}
return ret;
}
static int i2c_read_reg(struct i2c_client *client, u8 reg, u8 * val)
{
int err = 0;
struct i2c_msg msg[1];
unsigned char data[1];
if (!client->adapter) {
err = -ENODEV;
} else {
msg->addr = client->addr;
msg->flags = 0;
msg->len = 1;
msg->buf = data;
data[0] = reg;
err = i2c_transfer(client->adapter, msg, 1);
if (err >= 0) {
msg->flags = I2C_M_RD;
err = i2c_transfer(client->adapter, msg, 1);
if (err >= 0) {
*val = data[0];
}
}
}
return err;
}
static int i2c_write_reg(struct i2c_client *client, u8 reg, u8 val)
{
int err = 0;
struct i2c_msg msg[1];
unsigned char data[2];
if (!client->adapter) {
err = -ENODEV;
} else {
msg->addr = client->addr;
msg->flags = 0;
msg->len = 2;
msg->buf = data;
data[0] = reg;
data[1] = val;
err = i2c_transfer(client->adapter, msg, 1);
}
debug_print(KERN_INFO " i2c data write \n");
return err;
}
static int _i2c_attach_client(struct i2c_client *client,
struct i2c_driver *driver,
struct i2c_adapter *adap, int addr)
{
int err = 0;
if (client->adapter) {
err = -EBUSY; /* our client is already attached */
} else {
client->addr = addr;
/* client->flags = I2C_CLIENT_ALLOW_USE; */
client->driver = driver;
client->adapter = adap;
err = i2c_attach_client(client);
if (err) {
client->adapter = NULL;
}
}
return err;
}
static int _i2c_detach_client(struct i2c_client *client)
{
int err = 0;
if (!client->adapter) {
return -ENODEV; /* our client isn't attached */
} else {
err = i2c_detach_client(client);
client->adapter = NULL;
}
return err;
}
static int tvp5146_i2c_probe_adapter(struct i2c_adapter *adap)
{
return _i2c_attach_client(&tvp5146_i2c_client, &tvp5146_i2c_driver,
adap, TVP5146_I2C_ADDR);
}
static struct i2c_driver tvp5146_i2c_driver = {
.driver = {
.name = "tvp5146",
},
.id = I2C_DRIVERID_TVP5150,
.attach_adapter = tvp5146_i2c_probe_adapter,
.detach_client = _i2c_detach_client,
};
static int tvp5146_i2c_init(void)
{
int err;
struct i2c_driver *driver = &tvp5146_i2c_driver;
/* driver->owner = THIS_MODULE; */
/* strlcpy(driver->name, "TVP5146 Video Decoder I2C driver", */
/* sizeof(driver->name)); */
/* driver->id = I2C_DRIVERID_EXP0; */
/* driver->flags = I2C_DF_NOTIFY; */
/* driver->attach_adapter = tvp5146_i2c_probe_adapter; */
/* driver->detach_client = _i2c_detach_client; */
err = i2c_add_driver(driver);
if (err) {
debug_print(KERN_ERR
"Failed to register TVP5146 I2C client.\n");
}
debug_print(KERN_INFO "tvp5146 driver registered.\n");
return err;
}
static void tvp5146_i2c_cleanup(void)
{
struct i2c_driver *driver = &tvp5146_i2c_driver;
i2c_detach_client(&tvp5146_i2c_client);
i2c_del_driver(driver);
tvp5146_i2c_client.adapter = NULL;
}
module_init(tvp5146_i2c_init);
module_exit(tvp5146_i2c_cleanup);
EXPORT_SYMBOL(tvp5146_ctrl);
MODULE_LICENSE("GPL");
/**************************************************************************/
/* End of file */
/**************************************************************************/
/*
* Support functions for OMAP3 SDTI (Serial Debug Tracing Interface)
*
* Copyright (C) 2008 Nokia Corporation
* Written by: Roman Tereshonkov <roman.tereshonkov@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <mach/sti.h>
#include <asm/byteorder.h>
#include <asm/io.h>
#define SDTI_REVISION 0x000
#define SDTI_SYSCONFIG 0x010
#define SDTI_SYSSTATUS 0x014
#define SDTI_WINCTRL 0x024
#define SDTI_SCONFIG 0x028
#define SDTI_TESTCTRL 0x02C
#define SDTI_LOCK_ACCESS 0xFB0
#define CPU1_TRACE_EN 0x01
#define CPU2_TRACE_EN 0x02
#define SDTI_SYSCONFIG_SOFTRESET (1 << 1)
#define SDTI_SYSCONFIG_AUTOIDLE (1 << 0)
static struct clk *sdti_ck;
void __iomem *sti_base, *sti_channel_base;
static DEFINE_SPINLOCK(sdti_lock);
void sti_channel_write_trace(int len, int id, void *data,
unsigned int channel)
{
const u8 *tpntr = data;
spin_lock_irq(&sdti_lock);
sti_channel_writeb(id, channel);
while (len--)
sti_channel_writeb(*tpntr++, channel);
sti_channel_flush(channel);
spin_unlock_irq(&sdti_lock);
}
EXPORT_SYMBOL(sti_channel_write_trace);
static void omap_sdti_reset(void)
{
int i;
sti_writel(SDTI_SYSCONFIG_SOFTRESET, SDTI_SYSCONFIG);
for (i = 0; i < 10000; i++)
if (sti_readl(SDTI_SYSSTATUS) & 1)
break;
if (i == 10000)
printk(KERN_WARNING "XTI: no real reset\n");
}
static int __init omap_sdti_init(void)
{
char buf[64];
int i;
sdti_ck = clk_get(NULL, "emu_per_alwon_ck");
if (IS_ERR(sdti_ck)) {
printk(KERN_ERR "Cannot get clk emu_per_alwon_ck\n");
return PTR_ERR(sdti_ck);
}
clk_enable(sdti_ck);
omap_sdti_reset();
sti_writel(0xC5ACCE55, SDTI_LOCK_ACCESS);
/* Autoidle */
sti_writel(SDTI_SYSCONFIG_AUTOIDLE, SDTI_SYSCONFIG);
/* Claim SDTI */
sti_writel(1 << 30, SDTI_WINCTRL);
i = sti_readl(SDTI_WINCTRL);
if (!(i & (1 << 30)))
printk(KERN_WARNING "SDTI: cannot claim SDTI\n");
/* 4 bits dual, fclk/3 */
sti_writel(0x43, SDTI_SCONFIG);
/* CPU2 trace enable */
sti_writel(i | CPU2_TRACE_EN, SDTI_WINCTRL);
i = sti_readl(SDTI_WINCTRL);
/* Enable SDTI */
sti_writel((1 << 31) | (i & 0x3FFFFFFF), SDTI_WINCTRL);
i = sti_readl(SDTI_REVISION);
snprintf(buf, sizeof(buf), "OMAP SDTI support loaded (HW v%u.%u)\n",
(i >> 4) & 0x0f, i & 0x0f);
printk(KERN_INFO "%s", buf);
sti_channel_write_trace(strlen(buf), 0xc3, buf, 239);
return 0;
}
static void omap_sdti_exit(void)
{
sti_writel(0, SDTI_WINCTRL);
clk_disable(sdti_ck);
clk_put(sdti_ck);
}
static int __devinit omap_sdti_probe(struct platform_device *pdev)
{
struct resource *res, *cres;
unsigned int size;
if (pdev->num_resources != 2) {
dev_err(&pdev->dev, "invalid number of resources: %d\n",
pdev->num_resources);
return -ENODEV;
}
/* SDTI base */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!res)) {
dev_err(&pdev->dev, "invalid mem resource\n");
return -ENODEV;
}
/* Channel base */
cres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (unlikely(!cres)) {
dev_err(&pdev->dev, "invalid channel mem resource\n");
return -ENODEV;
}
size = res->end - res->start;
sti_base = ioremap(res->start, size);
if (unlikely(!sti_base))
return -ENODEV;
size = cres->end - cres->start;
sti_channel_base = ioremap(cres->start, size);
if (unlikely(!sti_channel_base)) {
iounmap(sti_base);
return -ENODEV;
}
return omap_sdti_init();
}
static int __devexit omap_sdti_remove(struct platform_device *pdev)
{
iounmap(sti_channel_base);
iounmap(sti_base);
omap_sdti_exit();
return 0;
}
static struct platform_driver omap_sdti_driver = {
.probe = omap_sdti_probe,
.remove = __devexit_p(omap_sdti_remove),
.driver = {
.name = "sti",
.owner = THIS_MODULE,
},
};
static int __init omap_sdti_module_init(void)
{
return platform_driver_register(&omap_sdti_driver);
}
static void __exit omap_sdti_module_exit(void)
{
platform_driver_unregister(&omap_sdti_driver);
}
subsys_initcall(omap_sdti_module_init);
module_exit(omap_sdti_module_exit);
MODULE_AUTHOR("Roman Tereshonkov");
MODULE_LICENSE("GPL");
......@@ -200,6 +200,14 @@ config MMC_MVSDIO
To compile this driver as a module, choose M here: the
module will be called mvsdio.
config MMC_DAVINCI
tristate "TI DAVINCI Multimedia Card Interface support"
depends on MMC
help
This selects the TI DAVINCI Multimedia card Interface.
If you have an DAVINCI board with a Multimedia Card slot,
say Y or M here. If unsure, say N.
config MMC_SPI
tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER && !HIGHMEM && HAS_DMA
......
......@@ -22,6 +22,7 @@ obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
......
/*
* davinci_mmc.c - TI DaVinci MMC/SD/SDIO driver
*
* Copyright (C) 2006 Texas Instruments.
* Original author: Purushotam Kumar
* Copyright (C) 2009 David Brownell
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mmc/host.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <mach/mmc.h>
#include <mach/edma.h>
/*
* Register Definitions
*/
#define DAVINCI_MMCCTL 0x00 /* Control Register */
#define DAVINCI_MMCCLK 0x04 /* Memory Clock Control Register */
#define DAVINCI_MMCST0 0x08 /* Status Register 0 */
#define DAVINCI_MMCST1 0x0C /* Status Register 1 */
#define DAVINCI_MMCIM 0x10 /* Interrupt Mask Register */
#define DAVINCI_MMCTOR 0x14 /* Response Time-Out Register */
#define DAVINCI_MMCTOD 0x18 /* Data Read Time-Out Register */
#define DAVINCI_MMCBLEN 0x1C /* Block Length Register */
#define DAVINCI_MMCNBLK 0x20 /* Number of Blocks Register */
#define DAVINCI_MMCNBLC 0x24 /* Number of Blocks Counter Register */
#define DAVINCI_MMCDRR 0x28 /* Data Receive Register */
#define DAVINCI_MMCDXR 0x2C /* Data Transmit Register */
#define DAVINCI_MMCCMD 0x30 /* Command Register */
#define DAVINCI_MMCARGHL 0x34 /* Argument Register */
#define DAVINCI_MMCRSP01 0x38 /* Response Register 0 and 1 */
#define DAVINCI_MMCRSP23 0x3C /* Response Register 0 and 1 */
#define DAVINCI_MMCRSP45 0x40 /* Response Register 0 and 1 */
#define DAVINCI_MMCRSP67 0x44 /* Response Register 0 and 1 */
#define DAVINCI_MMCDRSP 0x48 /* Data Response Register */
#define DAVINCI_MMCETOK 0x4C
#define DAVINCI_MMCCIDX 0x50 /* Command Index Register */
#define DAVINCI_MMCCKC 0x54
#define DAVINCI_MMCTORC 0x58
#define DAVINCI_MMCTODC 0x5C
#define DAVINCI_MMCBLNC 0x60
#define DAVINCI_SDIOCTL 0x64
#define DAVINCI_SDIOST0 0x68
#define DAVINCI_SDIOEN 0x6C
#define DAVINCI_SDIOST 0x70
#define DAVINCI_MMCFIFOCTL 0x74 /* FIFO Control Register */
/* DAVINCI_MMCCTL definitions */
#define MMCCTL_DATRST (1 << 0)
#define MMCCTL_CMDRST (1 << 1)
#define MMCCTL_WIDTH_4_BIT (1 << 2)
#define MMCCTL_DATEG_DISABLED (0 << 6)
#define MMCCTL_DATEG_RISING (1 << 6)
#define MMCCTL_DATEG_FALLING (2 << 6)
#define MMCCTL_DATEG_BOTH (3 << 6)
#define MMCCTL_PERMDR_LE (0 << 9)
#define MMCCTL_PERMDR_BE (1 << 9)
#define MMCCTL_PERMDX_LE (0 << 10)
#define MMCCTL_PERMDX_BE (1 << 10)
/* DAVINCI_MMCCLK definitions */
#define MMCCLK_CLKEN (1 << 8)
#define MMCCLK_CLKRT_MASK (0xFF << 0)
/* IRQ bit definitions, for DAVINCI_MMCST0 and DAVINCI_MMCIM */
#define MMCST0_DATDNE BIT(0) /* data done */
#define MMCST0_BSYDNE BIT(1) /* busy done */
#define MMCST0_RSPDNE BIT(2) /* command done */
#define MMCST0_TOUTRD BIT(3) /* data read timeout */
#define MMCST0_TOUTRS BIT(4) /* command response timeout */
#define MMCST0_CRCWR BIT(5) /* data write CRC error */
#define MMCST0_CRCRD BIT(6) /* data read CRC error */
#define MMCST0_CRCRS BIT(7) /* command response CRC error */
#define MMCST0_DXRDY BIT(9) /* data transmit ready (fifo empty) */
#define MMCST0_DRRDY BIT(10) /* data receive ready (data in fifo)*/
#define MMCST0_DATED BIT(11) /* DAT3 edge detect */
#define MMCST0_TRNDNE BIT(12) /* transfer done */
/* DAVINCI_MMCST1 definitions */
#define MMCST1_BUSY (1 << 0)
/* DAVINCI_MMCCMD definitions */
#define MMCCMD_CMD_MASK (0x3F << 0)
#define MMCCMD_PPLEN (1 << 7)
#define MMCCMD_BSYEXP (1 << 8)
#define MMCCMD_RSPFMT_MASK (3 << 9)
#define MMCCMD_RSPFMT_NONE (0 << 9)
#define MMCCMD_RSPFMT_R1456 (1 << 9)
#define MMCCMD_RSPFMT_R2 (2 << 9)
#define MMCCMD_RSPFMT_R3 (3 << 9)
#define MMCCMD_DTRW (1 << 11)
#define MMCCMD_STRMTP (1 << 12)
#define MMCCMD_WDATX (1 << 13)
#define MMCCMD_INITCK (1 << 14)
#define MMCCMD_DCLR (1 << 15)
#define MMCCMD_DMATRIG (1 << 16)
/* DAVINCI_MMCFIFOCTL definitions */
#define MMCFIFOCTL_FIFORST (1 << 0)
#define MMCFIFOCTL_FIFODIR_WR (1 << 1)
#define MMCFIFOCTL_FIFODIR_RD (0 << 1)
#define MMCFIFOCTL_FIFOLEV (1 << 2) /* 0 = 128 bits, 1 = 256 bits */
#define MMCFIFOCTL_ACCWD_4 (0 << 3) /* access width of 4 bytes */
#define MMCFIFOCTL_ACCWD_3 (1 << 3) /* access width of 3 bytes */
#define MMCFIFOCTL_ACCWD_2 (2 << 3) /* access width of 2 bytes */
#define MMCFIFOCTL_ACCWD_1 (3 << 3) /* access width of 1 byte */
/* MMCSD Init clock in Hz in opendain mode */
#define MMCSD_INIT_CLOCK 200000
/*
* One scatterlist dma "segment" is at most MAX_CCNT rw_threshold units,
* and we handle up to NR_SG segments. MMC_BLOCK_BOUNCE kicks in only
* for drivers with max_hw_segs == 1, making the segments bigger (64KB)
* than the page or two that's otherwise typical. NR_SG == 16 gives at
* least the same throughput boost, using EDMA transfer linkage instead
* of spending CPU time copying pages.
*/
#define MAX_CCNT ((1 << 16) - 1)
#define NR_SG 16
static unsigned rw_threshold = 32;
module_param(rw_threshold, uint, S_IRUGO);
MODULE_PARM_DESC(rw_threshold,
"Read/Write threshold. Default = 32");
static unsigned __initdata use_dma = 1;
module_param(use_dma, uint, 0);
MODULE_PARM_DESC(use_dma, "Whether to use DMA or not. Default = 1");
struct mmc_davinci_host {
struct mmc_command *cmd;
struct mmc_data *data;
struct mmc_host *mmc;
struct clk *clk;
unsigned int mmc_input_clk;
void __iomem *base;
struct resource *mem_res;
int irq;
unsigned char bus_mode;
#define DAVINCI_MMC_DATADIR_NONE 0
#define DAVINCI_MMC_DATADIR_READ 1
#define DAVINCI_MMC_DATADIR_WRITE 2
unsigned char data_dir;
/* buffer is used during PIO of one scatterlist segment, and
* is updated along with buffer_bytes_left. bytes_left applies
* to all N blocks of the PIO transfer.
*/
u8 *buffer;
u32 buffer_bytes_left;
u32 bytes_left;
u8 rxdma, txdma;
bool use_dma;
bool do_dma;
/* Scatterlist DMA uses one or more parameter RAM entries:
* the main one (associated with rxdma or txdma) plus zero or
* more links. The entries for a given transfer differ only
* by memory buffer (address, length) and link field.
*/
struct edmacc_param tx_template;
struct edmacc_param rx_template;
unsigned n_link;
u8 links[NR_SG - 1];
/* For PIO we walk scatterlists one segment at a time. */
unsigned int sg_len;
int sg_idx;
/* Version of the MMC/SD controller */
u8 version;
};
/* PIO only */
static void mmc_davinci_sg_to_buf(struct mmc_davinci_host *host)
{
struct scatterlist *sg;
sg = host->data->sg + host->sg_idx;
host->buffer_bytes_left = sg_dma_len(sg);
host->buffer = sg_virt(sg);
if (host->buffer_bytes_left > host->bytes_left)
host->buffer_bytes_left = host->bytes_left;
}
static void davinci_fifo_data_trans(struct mmc_davinci_host *host,
unsigned int n)
{
u8 *p;
unsigned int i;
if (host->buffer_bytes_left == 0) {
host->sg_idx++;
BUG_ON(host->sg_idx == host->sg_len);
mmc_davinci_sg_to_buf(host);
}
p = host->buffer;
if (n > host->buffer_bytes_left)
n = host->buffer_bytes_left;
host->buffer_bytes_left -= n;
host->bytes_left -= n;
/* NOTE: we never transfer more than rw_threshold bytes
* to/from the fifo here; there's no I/O overlap.
* This also assumes that access width( i.e. ACCWD) is 4 bytes
*/
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
for (i = 0; i < (n >> 2); i++) {
writel(*((u32 *)p), host->base + DAVINCI_MMCDXR);
p = p + 4;
}
if (n & 3) {
iowrite8_rep(host->base + DAVINCI_MMCDXR, p, (n & 3));
p = p + (n & 3);
}
} else {
for (i = 0; i < (n >> 2); i++) {
*((u32 *)p) = readl(host->base + DAVINCI_MMCDRR);
p = p + 4;
}
if (n & 3) {
ioread8_rep(host->base + DAVINCI_MMCDRR, p, (n & 3));
p = p + (n & 3);
}
}
host->buffer = p;
}
static void mmc_davinci_start_command(struct mmc_davinci_host *host,
struct mmc_command *cmd)
{
u32 cmd_reg = 0;
u32 im_val;
dev_dbg(mmc_dev(host->mmc), "CMD%d, arg 0x%08x%s\n",
cmd->opcode, cmd->arg,
({ char *s;
switch (mmc_resp_type(cmd)) {
case MMC_RSP_R1:
s = ", R1/R5/R6/R7 response";
break;
case MMC_RSP_R1B:
s = ", R1b response";
break;
case MMC_RSP_R2:
s = ", R2 response";
break;
case MMC_RSP_R3:
s = ", R3/R4 response";
break;
default:
s = ", (R? response)";
break;
}; s; }));
host->cmd = cmd;
switch (mmc_resp_type(cmd)) {
case MMC_RSP_R1B:
/* There's some spec confusion about when R1B is
* allowed, but if the card doesn't issue a BUSY
* then it's harmless for us to allow it.
*/
cmd_reg |= MMCCMD_BSYEXP;
/* FALLTHROUGH */
case MMC_RSP_R1: /* 48 bits, CRC */
cmd_reg |= MMCCMD_RSPFMT_R1456;
break;
case MMC_RSP_R2: /* 136 bits, CRC */
cmd_reg |= MMCCMD_RSPFMT_R2;
break;
case MMC_RSP_R3: /* 48 bits, no CRC */
cmd_reg |= MMCCMD_RSPFMT_R3;
break;
default:
cmd_reg |= MMCCMD_RSPFMT_NONE;
dev_dbg(mmc_dev(host->mmc), "unknown resp_type %04x\n",
mmc_resp_type(cmd));
break;
}
/* Set command index */
cmd_reg |= cmd->opcode;
/* Setting initialize clock */
if (cmd->opcode == 0)
cmd_reg |= MMCCMD_INITCK;
/* Enable EDMA transfer triggers */
if (host->do_dma)
cmd_reg |= MMCCMD_DMATRIG;
if (host->version == MMC_CTLR_VERSION_2 && host->data != NULL &&
host->data_dir == DAVINCI_MMC_DATADIR_READ)
cmd_reg |= MMCCMD_DMATRIG;
/* Setting whether command involves data transfer or not */
if (cmd->data)
cmd_reg |= MMCCMD_WDATX;
/* Setting whether stream or block transfer */
if (cmd->flags & MMC_DATA_STREAM)
cmd_reg |= MMCCMD_STRMTP;
/* Setting whether data read or write */
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
cmd_reg |= MMCCMD_DTRW;
if (host->bus_mode == MMC_BUSMODE_PUSHPULL)
cmd_reg |= MMCCMD_PPLEN;
/* set Command timeout */
writel(0xFFFF, host->base + DAVINCI_MMCTOR);
/* Enable interrupt (calculate here, defer until FIFO is stuffed). */
im_val = MMCST0_RSPDNE | MMCST0_CRCRS | MMCST0_TOUTRS;
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
im_val |= MMCST0_DATDNE | MMCST0_CRCWR;
if (!host->do_dma)
im_val |= MMCST0_DXRDY;
} else if (host->data_dir == DAVINCI_MMC_DATADIR_READ) {
im_val |= MMCST0_DATDNE | MMCST0_CRCRD | MMCST0_TOUTRD;
if (!host->do_dma)
im_val |= MMCST0_DRRDY;
}
/*
* Before non-DMA WRITE commands the controller needs priming:
* FIFO should be populated with 32 bytes
*/
if (!host->do_dma && (host->data_dir == DAVINCI_MMC_DATADIR_WRITE))
davinci_fifo_data_trans(host, 32);
writel(cmd->arg, host->base + DAVINCI_MMCARGHL);
writel(cmd_reg, host->base + DAVINCI_MMCCMD);
writel(im_val, host->base + DAVINCI_MMCIM);
}
/*----------------------------------------------------------------------*/
/* DMA infrastructure */
static void davinci_abort_dma(struct mmc_davinci_host *host)
{
int sync_dev;
if (host->data_dir == DAVINCI_MMC_DATADIR_READ)
sync_dev = host->rxdma;
else
sync_dev = host->txdma;
edma_stop(sync_dev);
edma_clean_channel(sync_dev);
}
static void
mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data);
static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data)
{
if (DMA_COMPLETE != ch_status) {
struct mmc_davinci_host *host = data;
/* Currently means: DMA Event Missed, or "null" transfer
* request was seen. In the future, TC errors (like bad
* addresses) might be presented too.
*/
dev_warn(mmc_dev(host->mmc), "DMA %s error\n",
(host->data->flags & MMC_DATA_WRITE)
? "write" : "read");
host->data->error = -EIO;
mmc_davinci_xfer_done(host, host->data);
}
}
/* Set up tx or rx template, to be modified and updated later */
static void __init mmc_davinci_dma_setup(struct mmc_davinci_host *host,
bool tx, struct edmacc_param *template)
{
unsigned sync_dev;
const u16 acnt = 4;
const u16 bcnt = rw_threshold >> 2;
const u16 ccnt = 0;
u32 src_port = 0;
u32 dst_port = 0;
s16 src_bidx, dst_bidx;
s16 src_cidx, dst_cidx;
/*
* A-B Sync transfer: each DMA request is for one "frame" of
* rw_threshold bytes, broken into "acnt"-size chunks repeated
* "bcnt" times. Each segment needs "ccnt" such frames; since
* we tell the block layer our mmc->max_seg_size limit, we can
* trust (later) that it's within bounds.
*
* The FIFOs are read/written in 4-byte chunks (acnt == 4) and
* EDMA will optimize memory operations to use larger bursts.
*/
if (tx) {
sync_dev = host->txdma;
/* src_prt, ccnt, and link to be set up later */
src_bidx = acnt;
src_cidx = acnt * bcnt;
dst_port = host->mem_res->start + DAVINCI_MMCDXR;
dst_bidx = 0;
dst_cidx = 0;
} else {
sync_dev = host->rxdma;
src_port = host->mem_res->start + DAVINCI_MMCDRR;
src_bidx = 0;
src_cidx = 0;
/* dst_prt, ccnt, and link to be set up later */
dst_bidx = acnt;
dst_cidx = acnt * bcnt;
}
/*
* We can't use FIFO mode for the FIFOs because MMC FIFO addresses
* are not 256-bit (32-byte) aligned. So we use INCR, and the W8BIT
* parameter is ignored.
*/
edma_set_src(sync_dev, src_port, INCR, W8BIT);
edma_set_dest(sync_dev, dst_port, INCR, W8BIT);
edma_set_src_index(sync_dev, src_bidx, src_cidx);
edma_set_dest_index(sync_dev, dst_bidx, dst_cidx);
edma_set_transfer_params(sync_dev, acnt, bcnt, ccnt, 8, ABSYNC);
edma_read_slot(sync_dev, template);
/* don't bother with irqs or chaining */
template->opt |= sync_dev << 12;
}
static void mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
struct mmc_data *data)
{
struct edmacc_param *template;
int channel, slot;
unsigned link;
struct scatterlist *sg;
unsigned sg_len;
unsigned bytes_left = host->bytes_left;
const unsigned shift = ffs(rw_threshold) - 1;
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
template = &host->tx_template;
channel = host->txdma;
} else {
template = &host->rx_template;
channel = host->rxdma;
}
/* We know sg_len and ccnt will never be out of range because
* we told the block layer to ensure that it only hands us one
* scatterlist segment per EDMA PARAM entry. Update the PARAM
* entries needed for each segment of this scatterlist.
*/
for (slot = channel, link = 0, sg = data->sg, sg_len = host->sg_len;
sg_len-- != 0 && bytes_left;
sg++, slot = host->links[link++]) {
u32 buf = sg_dma_address(sg);
unsigned count = sg_dma_len(sg);
template->link_bcntrld = sg_len
? (host->links[link] << 5)
: 0xffff;
if (count > bytes_left)
count = bytes_left;
bytes_left -= count;
if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
template->src = buf;
else
template->dst = buf;
template->ccnt = count >> shift;
edma_write_slot(slot, template);
}
if (host->version == MMC_CTLR_VERSION_2)
edma_clear_event(channel);
edma_start(channel);
}
static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
struct mmc_data *data)
{
int i;
int mask = rw_threshold - 1;
host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
((data->flags & MMC_DATA_WRITE)
? DMA_TO_DEVICE
: DMA_FROM_DEVICE));
/* no individual DMA segment should need a partial FIFO */
for (i = 0; i < host->sg_len; i++) {
if (sg_dma_len(data->sg + i) & mask) {
dma_unmap_sg(mmc_dev(host->mmc),
data->sg, data->sg_len,
(data->flags & MMC_DATA_WRITE)
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
return -1;
}
}
host->do_dma = 1;
mmc_davinci_send_dma_request(host, data);
return 0;
}
static void __init_or_module
davinci_release_dma_channels(struct mmc_davinci_host *host)
{
unsigned i;
if (!host->use_dma)
return;
for (i = 0; i < host->n_link; i++)
edma_free_slot(host->links[i]);
edma_free_channel(host->txdma);
edma_free_channel(host->rxdma);
}
static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
{
int r, i;
/* Acquire master DMA write channel */
r = edma_alloc_channel(host->txdma, mmc_davinci_dma_cb, host,
EVENTQ_DEFAULT);
if (r < 0) {
dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
"tx", r);
return r;
}
mmc_davinci_dma_setup(host, true, &host->tx_template);
/* Acquire master DMA read channel */
r = edma_alloc_channel(host->rxdma, mmc_davinci_dma_cb, host,
EVENTQ_DEFAULT);
if (r < 0) {
dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
"rx", r);
goto free_master_write;
}
mmc_davinci_dma_setup(host, false, &host->rx_template);
/* Allocate parameter RAM slots, which will later be bound to a
* channel as needed to handle a scatterlist.
*/
for (i = 0; i < ARRAY_SIZE(host->links); i++) {
r = edma_alloc_slot(EDMA_SLOT_ANY);
if (r < 0) {
dev_dbg(mmc_dev(host->mmc), "dma PaRAM alloc --> %d\n",
r);
break;
}
host->links[i] = r;
}
host->n_link = i;
return 0;
free_master_write:
edma_free_channel(host->txdma);
return r;
}
/*----------------------------------------------------------------------*/
static void
mmc_davinci_prepare_data(struct mmc_davinci_host *host, struct mmc_request *req)
{
int fifo_lev = (rw_threshold == 32) ? MMCFIFOCTL_FIFOLEV : 0;
int timeout;
struct mmc_data *data = req->data;
if (host->version == MMC_CTLR_VERSION_2)
fifo_lev = (rw_threshold == 64) ? MMCFIFOCTL_FIFOLEV : 0;
host->data = data;
if (data == NULL) {
host->data_dir = DAVINCI_MMC_DATADIR_NONE;
writel(0, host->base + DAVINCI_MMCBLEN);
writel(0, host->base + DAVINCI_MMCNBLK);
return;
}
dev_dbg(mmc_dev(host->mmc), "%s %s, %d blocks of %d bytes\n",
(data->flags & MMC_DATA_STREAM) ? "stream" : "block",
(data->flags & MMC_DATA_WRITE) ? "write" : "read",
data->blocks, data->blksz);
dev_dbg(mmc_dev(host->mmc), " DTO %d cycles + %d ns\n",
data->timeout_clks, data->timeout_ns);
/* Convert ns to clock cycles by assuming 20MHz frequency
* 1 cycle at 20MHz = 500 ns
*/
timeout = data->timeout_clks + data->timeout_ns / 500;
if (timeout > 0xffff)
timeout = 0xffff;
writel(timeout, host->base + DAVINCI_MMCTOD);
writel(data->blocks, host->base + DAVINCI_MMCNBLK);
writel(data->blksz, host->base + DAVINCI_MMCBLEN);
/* Configure the FIFO */
switch (data->flags & MMC_DATA_WRITE) {
case MMC_DATA_WRITE:
host->data_dir = DAVINCI_MMC_DATADIR_WRITE;
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR | MMCFIFOCTL_FIFORST,
host->base + DAVINCI_MMCFIFOCTL);
writel(fifo_lev | MMCFIFOCTL_FIFODIR_WR,
host->base + DAVINCI_MMCFIFOCTL);
break;
default:
host->data_dir = DAVINCI_MMC_DATADIR_READ;
writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD | MMCFIFOCTL_FIFORST,
host->base + DAVINCI_MMCFIFOCTL);
writel(fifo_lev | MMCFIFOCTL_FIFODIR_RD,
host->base + DAVINCI_MMCFIFOCTL);
break;
}
host->buffer = NULL;
host->bytes_left = data->blocks * data->blksz;
/* For now we try to use DMA whenever we won't need partial FIFO
* reads or writes, either for the whole transfer (as tested here)
* or for any individual scatterlist segment (tested when we call
* start_dma_transfer).
*
* While we *could* change that, unusual block sizes are rarely
* used. The occasional fallback to PIO should't hurt.
*/
if (host->use_dma && (host->bytes_left & (rw_threshold - 1)) == 0
&& mmc_davinci_start_dma_transfer(host, data) == 0) {
/* zero this to ensure we take no PIO paths */
host->bytes_left = 0;
} else {
/* Revert to CPU Copy */
host->sg_idx = 0;
host->sg_len = data->sg_len;
mmc_davinci_sg_to_buf(host);
}
}
static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req)
{
struct mmc_davinci_host *host = mmc_priv(mmc);
unsigned long timeout = jiffies + msecs_to_jiffies(900);
u32 mmcst1 = 0;
/* Card may still be sending BUSY after a previous operation,
* typically some kind of write. If so, we can't proceed yet.
*/
while (time_before(jiffies, timeout)) {
mmcst1 = readl(host->base + DAVINCI_MMCST1);
if (!(mmcst1 & MMCST1_BUSY))
break;
cpu_relax();
}
if (mmcst1 & MMCST1_BUSY) {
dev_err(mmc_dev(host->mmc), "still BUSY? bad ... \n");
req->cmd->error = -ETIMEDOUT;
mmc_request_done(mmc, req);
return;
}
host->do_dma = 0;
mmc_davinci_prepare_data(host, req);
mmc_davinci_start_command(host, req->cmd);
}
static unsigned int calculate_freq_for_card(struct mmc_davinci_host *host,
unsigned int mmc_req_freq)
{
unsigned int mmc_freq = 0, cpu_arm_clk = 0, mmc_push_pull = 0;
cpu_arm_clk = host->mmc_input_clk;
if (cpu_arm_clk > (2 * mmc_req_freq))
mmc_push_pull = ((unsigned int)cpu_arm_clk
/ (2 * mmc_req_freq)) - 1;
else
mmc_push_pull = 0;
mmc_freq = (unsigned int)cpu_arm_clk / (2 * (mmc_push_pull + 1));
if (mmc_freq > mmc_req_freq)
mmc_push_pull = mmc_push_pull + 1;
return mmc_push_pull;
}
static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
unsigned int open_drain_freq = 0, cpu_arm_clk = 0;
unsigned int mmc_push_pull_freq = 0;
struct mmc_davinci_host *host = mmc_priv(mmc);
cpu_arm_clk = host->mmc_input_clk;
dev_dbg(mmc_dev(host->mmc),
"clock %dHz busmode %d powermode %d Vdd %04x\n",
ios->clock, ios->bus_mode, ios->power_mode,
ios->vdd);
if (ios->bus_width == MMC_BUS_WIDTH_4) {
dev_dbg(mmc_dev(host->mmc), "Enabling 4 bit mode\n");
writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_WIDTH_4_BIT,
host->base + DAVINCI_MMCCTL);
} else {
dev_dbg(mmc_dev(host->mmc), "Disabling 4 bit mode\n");
writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_WIDTH_4_BIT,
host->base + DAVINCI_MMCCTL);
}
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
u32 temp;
open_drain_freq = ((unsigned int)cpu_arm_clk
/ (2 * MMCSD_INIT_CLOCK)) - 1;
temp = readl(host->base + DAVINCI_MMCCLK) & ~0xFF;
temp |= open_drain_freq;
writel(temp, host->base + DAVINCI_MMCCLK);
} else {
u32 temp;
mmc_push_pull_freq = calculate_freq_for_card(host, ios->clock);
temp = readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKEN;
writel(temp, host->base + DAVINCI_MMCCLK);
udelay(10);
temp = readl(host->base + DAVINCI_MMCCLK) & ~MMCCLK_CLKRT_MASK;
temp |= mmc_push_pull_freq;
writel(temp, host->base + DAVINCI_MMCCLK);
writel(temp | MMCCLK_CLKEN, host->base + DAVINCI_MMCCLK);
udelay(10);
}
host->bus_mode = ios->bus_mode;
if (ios->power_mode == MMC_POWER_UP) {
unsigned long timeout = jiffies + msecs_to_jiffies(50);
bool lose = true;
/* Send clock cycles, poll completion */
writel(0, host->base + DAVINCI_MMCARGHL);
writel(MMCCMD_INITCK, host->base + DAVINCI_MMCCMD);
while (time_before(jiffies, timeout)) {
u32 tmp = readl(host->base + DAVINCI_MMCST0);
if (tmp & MMCST0_RSPDNE) {
lose = false;
break;
}
cpu_relax();
}
if (lose)
dev_warn(mmc_dev(host->mmc), "powerup timeout\n");
}
/* FIXME on power OFF, reset things ... */
}
static void
mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data)
{
host->data = NULL;
host->data_dir = DAVINCI_MMC_DATADIR_NONE;
if (host->do_dma) {
davinci_abort_dma(host);
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
(data->flags & MMC_DATA_WRITE)
? DMA_TO_DEVICE
: DMA_FROM_DEVICE);
host->do_dma = false;
}
if (!data->stop || (host->cmd && host->cmd->error)) {
mmc_request_done(host->mmc, data->mrq);
writel(0, host->base + DAVINCI_MMCIM);
} else
mmc_davinci_start_command(host, data->stop);
}
static void mmc_davinci_cmd_done(struct mmc_davinci_host *host,
struct mmc_command *cmd)
{
host->cmd = NULL;
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
/* response type 2 */
cmd->resp[3] = readl(host->base + DAVINCI_MMCRSP01);
cmd->resp[2] = readl(host->base + DAVINCI_MMCRSP23);
cmd->resp[1] = readl(host->base + DAVINCI_MMCRSP45);
cmd->resp[0] = readl(host->base + DAVINCI_MMCRSP67);
} else {
/* response types 1, 1b, 3, 4, 5, 6 */
cmd->resp[0] = readl(host->base + DAVINCI_MMCRSP67);
}
}
if (host->data == NULL || cmd->error) {
if (cmd->error == -ETIMEDOUT)
cmd->mrq->cmd->retries = 0;
mmc_request_done(host->mmc, cmd->mrq);
writel(0, host->base + DAVINCI_MMCIM);
}
}
static void
davinci_abort_data(struct mmc_davinci_host *host, struct mmc_data *data)
{
u32 temp;
/* record how much data we transferred */
temp = readl(host->base + DAVINCI_MMCNBLC);
data->bytes_xfered += (data->blocks - temp) * data->blksz;
/* reset command and data state machines */
temp = readl(host->base + DAVINCI_MMCCTL);
writel(temp | MMCCTL_CMDRST | MMCCTL_DATRST,
host->base + DAVINCI_MMCCTL);
temp &= ~(MMCCTL_CMDRST | MMCCTL_DATRST);
udelay(10);
writel(temp, host->base + DAVINCI_MMCCTL);
}
static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
{
struct mmc_davinci_host *host = (struct mmc_davinci_host *)dev_id;
unsigned int status, qstatus;
int end_command = 0;
int end_transfer = 0;
struct mmc_data *data = host->data;
if (host->cmd == NULL && host->data == NULL) {
status = readl(host->base + DAVINCI_MMCST0);
dev_dbg(mmc_dev(host->mmc),
"Spurious interrupt 0x%04x\n", status);
/* Disable the interrupt from mmcsd */
writel(0, host->base + DAVINCI_MMCIM);
return IRQ_NONE;
}
status = readl(host->base + DAVINCI_MMCST0);
qstatus = status;
/* handle FIFO first when using PIO for data.
* bytes_left will decrease to zero as I/O progress and status will
* read zero over iteration because this controller status
* register(MMCST0) reports any status only once and it is cleared
* by read. So, it is not unbouned loop even in the case of
* non-dma.
*/
while (host->bytes_left && (status & (MMCST0_DXRDY | MMCST0_DRRDY))) {
davinci_fifo_data_trans(host, rw_threshold);
status = readl(host->base + DAVINCI_MMCST0);
if (!status)
break;
qstatus |= status;
}
if (qstatus & MMCST0_DATDNE) {
/* All blocks sent/received, and CRC checks passed */
if (data != NULL) {
if ((host->do_dma == 0) && (host->bytes_left > 0)) {
/* if datasize < rw_threshold
* no RX ints are generated
*/
davinci_fifo_data_trans(host, host->bytes_left);
}
end_transfer = 1;
data->bytes_xfered += data->blocks * data->blksz;
} else {
dev_err(mmc_dev(host->mmc),
"DATDNE with no host->data\n");
}
}
if (qstatus & MMCST0_TOUTRD) {
/* Read data timeout */
data->error = -ETIMEDOUT;
end_transfer = 1;
dev_dbg(mmc_dev(host->mmc),
"read data timeout, status %x\n",
qstatus);
davinci_abort_data(host, data);
}
if (qstatus & (MMCST0_CRCWR | MMCST0_CRCRD)) {
/* Data CRC error */
data->error = -EILSEQ;
end_transfer = 1;
/* NOTE: this controller uses CRCWR to report both CRC
* errors and timeouts (on writes). MMCDRSP values are
* only weakly documented, but 0x9f was clearly a timeout
* case and the two three-bit patterns in various SD specs
* (101, 010) aren't part of it ...
*/
if (qstatus & MMCST0_CRCWR) {
u32 temp = readb(host->base + DAVINCI_MMCDRSP);
if (temp == 0x9f)
data->error = -ETIMEDOUT;
}
dev_dbg(mmc_dev(host->mmc), "data %s %s error\n",
(qstatus & MMCST0_CRCWR) ? "write" : "read",
(data->error == -ETIMEDOUT) ? "timeout" : "CRC");
davinci_abort_data(host, data);
}
if (qstatus & MMCST0_TOUTRS) {
/* Command timeout */
if (host->cmd) {
dev_dbg(mmc_dev(host->mmc),
"CMD%d timeout, status %x\n",
host->cmd->opcode, qstatus);
host->cmd->error = -ETIMEDOUT;
if (data) {
end_transfer = 1;
davinci_abort_data(host, data);
} else
end_command = 1;
}
}
if (qstatus & MMCST0_CRCRS) {
/* Command CRC error */
dev_dbg(mmc_dev(host->mmc), "Command CRC error\n");
if (host->cmd) {
/* Ignore CMD CRC errors during high speed operation */
if (host->mmc->ios.clock <= 25000000)
host->cmd->error = -EILSEQ;
end_command = 1;
}
}
if (qstatus & MMCST0_RSPDNE) {
/* End of command phase */
end_command = (int) host->cmd;
}
if (end_command)
mmc_davinci_cmd_done(host, host->cmd);
if (end_transfer)
mmc_davinci_xfer_done(host, data);
return IRQ_HANDLED;
}
static int mmc_davinci_get_cd(struct mmc_host *mmc)
{
struct platform_device *pdev = to_platform_device(mmc->parent);
struct davinci_mmc_config *config = pdev->dev.platform_data;
if (!config || !config->get_cd)
return -ENOSYS;
return config->get_cd(pdev->id);
}
static int mmc_davinci_get_ro(struct mmc_host *mmc)
{
struct platform_device *pdev = to_platform_device(mmc->parent);
struct davinci_mmc_config *config = pdev->dev.platform_data;
if (!config || !config->get_ro)
return -ENOSYS;
return config->get_ro(pdev->id);
}
static struct mmc_host_ops mmc_davinci_ops = {
.request = mmc_davinci_request,
.set_ios = mmc_davinci_set_ios,
.get_cd = mmc_davinci_get_cd,
.get_ro = mmc_davinci_get_ro,
};
/*----------------------------------------------------------------------*/
static void __init init_mmcsd_host(struct mmc_davinci_host *host)
{
/* DAT line portion is diabled and in reset state */
writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_DATRST,
host->base + DAVINCI_MMCCTL);
/* CMD line portion is diabled and in reset state */
writel(readl(host->base + DAVINCI_MMCCTL) | MMCCTL_CMDRST,
host->base + DAVINCI_MMCCTL);
udelay(10);
writel(0, host->base + DAVINCI_MMCCLK);
writel(MMCCLK_CLKEN, host->base + DAVINCI_MMCCLK);
writel(0xFFFF, host->base + DAVINCI_MMCTOR);
writel(0xFFFF, host->base + DAVINCI_MMCTOD);
writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_DATRST,
host->base + DAVINCI_MMCCTL);
writel(readl(host->base + DAVINCI_MMCCTL) & ~MMCCTL_CMDRST,
host->base + DAVINCI_MMCCTL);
udelay(10);
}
static int __init davinci_mmcsd_probe(struct platform_device *pdev)
{
struct davinci_mmc_config *pdata = pdev->dev.platform_data;
struct mmc_davinci_host *host = NULL;
struct mmc_host *mmc = NULL;
struct resource *r, *mem = NULL;
int ret = 0, irq = 0;
size_t mem_size;
/* REVISIT: when we're fully converted, fail if pdata is NULL */
ret = -ENODEV;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (!r || irq == NO_IRQ)
goto out;
ret = -EBUSY;
mem_size = r->end - r->start + 1;
mem = request_mem_region(r->start, mem_size, pdev->name);
if (!mem)
goto out;
ret = -ENOMEM;
mmc = mmc_alloc_host(sizeof(struct mmc_davinci_host), &pdev->dev);
if (!mmc)
goto out;
host = mmc_priv(mmc);
host->mmc = mmc; /* Important */
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!r)
goto out;
host->rxdma = r->start;
r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!r)
goto out;
host->txdma = r->start;
host->mem_res = mem;
host->base = ioremap(mem->start, mem_size);
if (!host->base)
goto out;
ret = -ENXIO;
host->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
goto out;
}
clk_enable(host->clk);
host->mmc_input_clk = clk_get_rate(host->clk);
init_mmcsd_host(host);
host->use_dma = use_dma;
host->irq = irq;
if (host->use_dma && davinci_acquire_dma_channels(host) != 0)
host->use_dma = 0;
/* REVISIT: someday, support IRQ-driven card detection. */
mmc->caps |= MMC_CAP_NEEDS_POLL;
if (!pdata || pdata->wires == 4 || pdata->wires == 0)
mmc->caps |= MMC_CAP_4_BIT_DATA;
host->version = pdata->version;
mmc->ops = &mmc_davinci_ops;
mmc->f_min = 312500;
mmc->f_max = 25000000;
if (pdata->max_freq)
mmc->f_max = pdata->max_freq;
if (pdata->caps)
mmc->caps |= pdata->caps;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
/* With no iommu coalescing pages, each phys_seg is a hw_seg.
* Each hw_seg uses one EDMA parameter RAM slot, always one
* channel and then usually some linked slots.
*/
mmc->max_hw_segs = 1 + host->n_link;
mmc->max_phys_segs = mmc->max_hw_segs;
/* EDMA limit per hw segment (one or two MBytes) */
mmc->max_seg_size = MAX_CCNT * rw_threshold;
/* MMC/SD controller limits for multiblock requests */
mmc->max_blk_size = 4095; /* BLEN is 11 bits */
mmc->max_blk_count = 65535; /* NBLK is 16 bits */
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
dev_dbg(mmc_dev(host->mmc), "max_phys_segs=%d\n", mmc->max_phys_segs);
dev_dbg(mmc_dev(host->mmc), "max_hw_segs=%d\n", mmc->max_hw_segs);
dev_dbg(mmc_dev(host->mmc), "max_blk_size=%d\n", mmc->max_blk_size);
dev_dbg(mmc_dev(host->mmc), "max_req_size=%d\n", mmc->max_req_size);
dev_dbg(mmc_dev(host->mmc), "max_seg_size=%d\n", mmc->max_seg_size);
platform_set_drvdata(pdev, host);
ret = mmc_add_host(mmc);
if (ret < 0)
goto out;
ret = request_irq(irq, mmc_davinci_irq, 0, mmc_hostname(mmc), host);
if (ret)
goto out;
rename_region(mem, mmc_hostname(mmc));
dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n",
host->use_dma ? "DMA" : "PIO",
(mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
return 0;
out:
if (host) {
davinci_release_dma_channels(host);
if (host->clk) {
clk_disable(host->clk);
clk_put(host->clk);
}
if (host->base)
iounmap(host->base);
}
if (mmc)
mmc_free_host(mmc);
if (mem)
release_resource(mem);
dev_dbg(&pdev->dev, "probe err %d\n", ret);
return ret;
}
static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
{
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (host) {
mmc_remove_host(host->mmc);
free_irq(host->irq, host);
davinci_release_dma_channels(host);
clk_disable(host->clk);
clk_put(host->clk);
iounmap(host->base);
release_resource(host->mem_res);
mmc_free_host(host->mmc);
}
return 0;
}
#ifdef CONFIG_PM
static int davinci_mmcsd_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
return mmc_suspend_host(host->mmc, msg);
}
static int davinci_mmcsd_resume(struct platform_device *pdev)
{
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
return mmc_resume_host(host->mmc);
}
#else
#define davinci_mmcsd_suspend NULL
#define davinci_mmcsd_resume NULL
#endif
static struct platform_driver davinci_mmcsd_driver = {
.driver = {
.name = "davinci_mmc",
.owner = THIS_MODULE,
},
.remove = __exit_p(davinci_mmcsd_remove),
.suspend = davinci_mmcsd_suspend,
.resume = davinci_mmcsd_resume,
};
static int __init davinci_mmcsd_init(void)
{
return platform_driver_probe(&davinci_mmcsd_driver,
davinci_mmcsd_probe);
}
module_init(davinci_mmcsd_init);
static void __exit davinci_mmcsd_exit(void)
{
platform_driver_unregister(&davinci_mmcsd_driver);
}
module_exit(davinci_mmcsd_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MMC/SD driver for Davinci MMC controller");
......@@ -407,16 +407,17 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
}
info->chip.ecc.mode = ecc_mode;
info->clk = clk_get(&pdev->dev, "AEMIFCLK");
info->clk = clk_get(&pdev->dev, "aemif");
if (IS_ERR(info->clk)) {
ret = PTR_ERR(info->clk);
dev_dbg(&pdev->dev, "unable to get AEMIFCLK, err %d\n", ret);
dev_dbg(&pdev->dev, "unable to get AEMIF clock, err %d\n", ret);
goto err_clk;
}
ret = clk_enable(info->clk);
if (ret < 0) {
dev_dbg(&pdev->dev, "unable to enable AEMIFCLK, err %d\n", ret);
dev_dbg(&pdev->dev, "unable to enable AEMIF clock, err %d\n",
ret);
goto err_clk_enable;
}
......
......@@ -2671,6 +2671,13 @@ int nand_scan_tail(struct mtd_info *mtd)
*/
switch (chip->ecc.mode) {
#ifdef CONFIG_NAND_FLASH_HW_ECC
case NAND_ECC_HW12_2048:
case NAND_ECC_HW8_512:
case NAND_ECC_HW6_512:
case NAND_ECC_HW3_512:
case NAND_ECC_HW3_256:
#endif
case NAND_ECC_HW:
/* Use standard hwecc read page function ? */
if (!chip->ecc.read_page)
......
......@@ -917,6 +917,16 @@ config SMC91X
The module will be called smc91x. If you want to compile it as a
module, say M here and read <file:Documentation/kbuild/modules.txt>.
config TI_DAVINCI_EMAC
tristate "TI DaVinci EMAC Support"
depends on ARM && ARCH_DAVINCI
select PHYLIB
help
This driver supports TI's DaVinci Ethernet .
To compile this driver as a module, choose M here: the module
will be called ti_davinci_emac. This is recommended.
config NET_NETX
tristate "NetX Ethernet support"
select MII
......
......@@ -2,6 +2,9 @@
# Makefile for the Linux network (ethercard) device drivers.
#
davinci_emac_driver-objs := davinci_emac.o
obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac_driver.o
obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_E1000E) += e1000e/
obj-$(CONFIG_IBM_NEW_EMAC) += ibm_newemac/
......
/*
* DaVinci EMAC Ethernet controller
*
* DaVinci EMAC ethernet controller is based upon CPPI 3.0 TI DMA engine
* and this driver supports the following devices: DM644x as of now
*
* Copyright (C) 2008 Texas Instruments.
*
* ---------------------------------------------------------------------------
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* ---------------------------------------------------------------------------
* History:
* 0-5 A number of folks worked on this driver in bits and pieces but the major
* contribution came from Suraj Iyer and Anant Gole
* 6.0 Anant Gole - rewrote the driver as per Linux conventions
*/
/** Pending Items in this driver:
* 1. Use Linux cache infrastcture for DMA'ed memory (dma_xxx functions)
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/in.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/highmem.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/version.h>
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/semaphore.h>
#include <linux/phy.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <mach/emac.h>
static int cfg_link_speed;
module_param(cfg_link_speed, int, 0);
MODULE_PARM_DESC(cfg_link_speed,
"EMAC link speed : <0=Auto, 1=No Phy, 10, 100, 1000 mbps");
static int cfg_link_duplex;
module_param(cfg_link_duplex, int, 0);
MODULE_PARM_DESC(cfg_link_duplex,
"EMAC link duplex: <0=Auto, 1=Unknown, 2=Half, 3=Full");
static int debug_level;
module_param(debug_level, int, 0);
MODULE_PARM_DESC(debug_level, "DaVinci EMAC debug level (NETIF_MSG bits)");
/* Netif debug messages possible */
#define DAVINCI_EMAC_DEBUG (NETIF_MSG_DRV | \
NETIF_MSG_PROBE | \
NETIF_MSG_LINK | \
NETIF_MSG_TIMER | \
NETIF_MSG_IFDOWN | \
NETIF_MSG_IFUP | \
NETIF_MSG_RX_ERR | \
NETIF_MSG_TX_ERR | \
NETIF_MSG_TX_QUEUED | \
NETIF_MSG_INTR | \
NETIF_MSG_TX_DONE | \
NETIF_MSG_RX_STATUS | \
NETIF_MSG_PKTDATA | \
NETIF_MSG_HW | \
NETIF_MSG_WOL)
/* version info */
#define EMAC_MAJOR_VERSION 6
#define EMAC_MINOR_VERSION 0
#define EMAC_MODULE_VERSION "6.0"
MODULE_VERSION(EMAC_MODULE_VERSION);
static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.0";
/* Configuration items */
#define EMAC_MDIO_DEBUG (0) /* use this debug with caution */
#define EMAC_DEF_PASS_CRC (0) /* Do not pass CRC upto frames */
#define EMAC_DEF_QOS_EN (0) /* EMAC proprietary QoS disabled */
#define EMAC_DEF_NO_BUFF_CHAIN (0) /* No buffer chain */
#define EMAC_DEF_MACCTRL_FRAME_EN (0) /* Discard Maccontrol frames */
#define EMAC_DEF_SHORT_FRAME_EN (0) /* Discard short frames */
#define EMAC_DEF_ERROR_FRAME_EN (0) /* Discard error frames */
#define EMAC_DEF_PROM_EN (0) /* Promiscous disabled */
#define EMAC_DEF_PROM_CH (0) /* Promiscous channel is 0 */
#define EMAC_DEF_BCAST_EN (1) /* Broadcast enabled */
#define EMAC_DEF_BCAST_CH (0) /* Broadcast channel is 0 */
#define EMAC_DEF_MCAST_EN (1) /* Multicast enabled */
#define EMAC_DEF_MCAST_CH (0) /* Multicast channel is 0 */
#define EMAC_DEF_TXPRIO_FIXED (1) /* TX Priority is fixed */
#define EMAC_DEF_TXPACING_EN (0) /* TX pacing NOT supported*/
#define EMAC_DEF_BUFFER_OFFSET (0) /* Buffer offset to DMA (future) */
#define EMAC_DEF_MIN_ETHPKTSIZE (60) /* Minimum ethernet pkt size */
#define EMAC_DEF_MAX_FRAME_SIZE (1500 + 14 + 4 + 4)
#define EMAC_DEF_TX_CH (0) /* Default 0th channel */
#define EMAC_DEF_RX_CH (0) /* Default 0th channel */
#define EMAC_DEF_MDIO_TICK_MS (10) /* typically 1 tick=1 ms) */
#define EMAC_DEF_MAX_TX_CH (1) /* Max TX channels configured */
#define EMAC_DEF_MAX_RX_CH (1) /* Max RX channels configured */
#define EMAC_POLL_WEIGHT (64) /* Default NAPI poll weight */
/* Buffer descriptor parameters */
#define EMAC_DEF_TX_MAX_SERVICE (32) /* TX max service BD's */
#define EMAC_DEF_RX_MAX_SERVICE (64) /* should = netdev->weight */
/* config parameters that get tested for operation */
#define EMAC_MIN_FREQUENCY_FOR_10MBPS (5500000)
#define EMAC_MIN_FREQUENCY_FOR_100MBPS (55000000)
#define EMAC_MIN_FREQUENCY_FOR_1000MBPS (125000000)
/* EMAC register related defines */
#define EMAC_ALL_MULTI_REG_VALUE (0xFFFFFFFF)
#define EMAC_NUM_MULTICAST_BITS (64)
#define EMAC_TEARDOWN_VALUE (0xFFFFFFFC)
#define EMAC_TX_CONTROL_TX_ENABLE_VAL (0x1)
#define EMAC_RX_CONTROL_RX_ENABLE_VAL (0x1)
#define EMAC_MAC_HOST_ERR_INTMASK_VAL (0x2)
#define EMAC_RX_UNICAST_CLEAR_ALL (0xFF)
#define EMAC_INT_MASK_CLEAR (0xFF)
/* RX MBP register bit positions */
#define EMAC_RXMBP_PASSCRC_MASK (0x1 << 30)
#define EMAC_RXMBP_QOSEN_MASK (0x1 << 29)
#define EMAC_RXMBP_NOCHAIN_MASK (0x1 << 28)
#define EMAC_RXMBP_CMFEN_MASK (0x1 << 24)
#define EMAC_RXMBP_CSFEN_MASK (0x1 << 23)
#define EMAC_RXMBP_CEFEN_MASK (0x1 << 22)
#define EMAC_RXMBP_CAFEN_MASK (0x1 << 21)
#define EMAC_RXMBP_PROMCH_SHIFT (16)
#define EMAC_RXMBP_PROMCH_MASK (0x7 << 16)
#define EMAC_RXMBP_BROADEN_MASK (0x1 << 13)
#define EMAC_RXMBP_BROADCH_SHIFT (8)
#define EMAC_RXMBP_BROADCH_MASK (0x7 << 8)
#define EMAC_RXMBP_MULTIEN_MASK (0x1 << 5)
#define EMAC_RXMBP_MULTICH_SHIFT (0)
#define EMAC_RXMBP_MULTICH_MASK (0x7)
#define EMAC_RXMBP_CHMASK (0x7)
/* EMAC register definitions/bit maps used */
# define EMAC_MBP_RXPROMISC (0x00200000)
# define EMAC_MBP_PROMISCCH(ch) (((ch) & 0x7) << 16)
# define EMAC_MBP_RXBCAST (0x00002000)
# define EMAC_MBP_BCASTCHAN(ch) (((ch) & 0x7) << 8)
# define EMAC_MBP_RXMCAST (0x00000020)
# define EMAC_MBP_MCASTCHAN(ch) ((ch) & 0x7)
/* EMAC mac_control register */
#define EMAC_MACCONTROL_TXPTYPE (0x200)
#define EMAC_MACCONTROL_TXPACEEN (0x40)
#define EMAC_MACCONTROL_MIIEN (0x20)
#define EMAC_MACCONTROL_GIGABITEN (0x80)
#define EMAC_MACCONTROL_GIGABITEN_SHIFT (7)
#define EMAC_MACCONTROL_FULLDUPLEXEN (0x1)
#define EMAC_MACCONTROL_RMIISPEED_MASK BIT(15)
/* GIGABIT MODE related bits */
#define EMAC_DM646X_MACCONTORL_GMIIEN (0x01 << 5)
#define EMAC_DM646X_MACCONTORL_GIG (0x01 << 7)
#define EMAC_DM646X_MACCONTORL_GIGFORCE (0x01 << 17)
/* EMAC mac_status register */
#define EMAC_MACSTATUS_TXERRCODE_MASK (0xF00000)
#define EMAC_MACSTATUS_TXERRCODE_SHIFT (20)
#define EMAC_MACSTATUS_TXERRCH_MASK (0x7)
#define EMAC_MACSTATUS_TXERRCH_SHIFT (16)
#define EMAC_MACSTATUS_RXERRCODE_MASK (0xF000)
#define EMAC_MACSTATUS_RXERRCODE_SHIFT (12)
#define EMAC_MACSTATUS_RXERRCH_MASK (0x7)
#define EMAC_MACSTATUS_RXERRCH_SHIFT (8)
/* EMAC RX register masks */
#define EMAC_RX_MAX_LEN_MASK (0xFFFF)
#define EMAC_RX_BUFFER_OFFSET_MASK (0xFFFF)
/* MAC_IN_VECTOR (0x180) register bit fields */
#define EMAC_DM644X_MAC_IN_VECTOR_HOST_INT (0x20000)
#define EMAC_DM644X_MAC_IN_VECTOR_STATPEND_INT (0x10000)
#define EMAC_DM644X_MAC_IN_VECTOR_RX_INT_VEC (0x0100)
#define EMAC_DM644X_MAC_IN_VECTOR_TX_INT_VEC (0x01)
/** NOTE:: For DM646x the IN_VECTOR has changed */
#define EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC (1 << EMAC_DEF_RX_CH)
#define EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC (0x10000 << EMAC_DEF_TX_CH)
/* CPPI bit positions */
#define EMAC_CPPI_SOP_BIT (1 << 31)
#define EMAC_CPPI_EOP_BIT (1 << 30)
#define EMAC_CPPI_OWNERSHIP_BIT (1 << 29)
#define EMAC_CPPI_EOQ_BIT (1 << 28)
#define EMAC_CPPI_TEARDOWN_COMPLETE_BIT (1 << 27)
#define EMAC_CPPI_PASS_CRC_BIT (1 << 26)
#define EMAC_RX_BD_BUF_SIZE (0xFFFF)
#define EMAC_BD_LENGTH_FOR_CACHE (16) /* only CPPI bytes */
#define EMAC_RX_BD_PKT_LENGTH_MASK (0xFFFF)
/* Max hardware defines */
#define EMAC_MAX_TXRX_CHANNELS (8) /* Max hardware channels */
#define EMAC_DEF_MAX_MULTICAST_ADDRESSES (64) /* Max mcast addr's */
/* EMAC Peripheral Device Register Memory Layout structure */
#define EMAC_TXIDVER 0x0
#define EMAC_TXCONTROL 0x4
#define EMAC_TXTEARDOWN 0x8
#define EMAC_RXIDVER 0x10
#define EMAC_RXCONTROL 0x14
#define EMAC_RXTEARDOWN 0x18
#define EMAC_TXINTSTATRAW 0x80
#define EMAC_TXINTSTATMASKED 0x84
#define EMAC_TXINTMASKSET 0x88
#define EMAC_TXINTMASKCLEAR 0x8C
#define EMAC_MACINVECTOR 0x90
#define EMAC_DM646X_MACEOIVECTOR 0x94
#define EMAC_RXINTSTATRAW 0xA0
#define EMAC_RXINTSTATMASKED 0xA4
#define EMAC_RXINTMASKSET 0xA8
#define EMAC_RXINTMASKCLEAR 0xAC
#define EMAC_MACINTSTATRAW 0xB0
#define EMAC_MACINTSTATMASKED 0xB4
#define EMAC_MACINTMASKSET 0xB8
#define EMAC_MACINTMASKCLEAR 0xBC
#define EMAC_RXMBPENABLE 0x100
#define EMAC_RXUNICASTSET 0x104
#define EMAC_RXUNICASTCLEAR 0x108
#define EMAC_RXMAXLEN 0x10C
#define EMAC_RXBUFFEROFFSET 0x110
#define EMAC_RXFILTERLOWTHRESH 0x114
#define EMAC_MACCONTROL 0x160
#define EMAC_MACSTATUS 0x164
#define EMAC_EMCONTROL 0x168
#define EMAC_FIFOCONTROL 0x16C
#define EMAC_MACCONFIG 0x170
#define EMAC_SOFTRESET 0x174
#define EMAC_MACSRCADDRLO 0x1D0
#define EMAC_MACSRCADDRHI 0x1D4
#define EMAC_MACHASH1 0x1D8
#define EMAC_MACHASH2 0x1DC
#define EMAC_MACADDRLO 0x500
#define EMAC_MACADDRHI 0x504
#define EMAC_MACINDEX 0x508
/* EMAC HDP and Completion registors */
#define EMAC_TXHDP(ch) (0x600 + (ch * 4))
#define EMAC_RXHDP(ch) (0x620 + (ch * 4))
#define EMAC_TXCP(ch) (0x640 + (ch * 4))
#define EMAC_RXCP(ch) (0x660 + (ch * 4))
/* EMAC statistics registers */
#define EMAC_RXGOODFRAMES 0x200
#define EMAC_RXBCASTFRAMES 0x204
#define EMAC_RXMCASTFRAMES 0x208
#define EMAC_RXPAUSEFRAMES 0x20C
#define EMAC_RXCRCERRORS 0x210
#define EMAC_RXALIGNCODEERRORS 0x214
#define EMAC_RXOVERSIZED 0x218
#define EMAC_RXJABBER 0x21C
#define EMAC_RXUNDERSIZED 0x220
#define EMAC_RXFRAGMENTS 0x224
#define EMAC_RXFILTERED 0x228
#define EMAC_RXQOSFILTERED 0x22C
#define EMAC_RXOCTETS 0x230
#define EMAC_TXGOODFRAMES 0x234
#define EMAC_TXBCASTFRAMES 0x238
#define EMAC_TXMCASTFRAMES 0x23C
#define EMAC_TXPAUSEFRAMES 0x240
#define EMAC_TXDEFERRED 0x244
#define EMAC_TXCOLLISION 0x248
#define EMAC_TXSINGLECOLL 0x24C
#define EMAC_TXMULTICOLL 0x250
#define EMAC_TXEXCESSIVECOLL 0x254
#define EMAC_TXLATECOLL 0x258
#define EMAC_TXUNDERRUN 0x25C
#define EMAC_TXCARRIERSENSE 0x260
#define EMAC_TXOCTETS 0x264
#define EMAC_NETOCTETS 0x280
#define EMAC_RXSOFOVERRUNS 0x284
#define EMAC_RXMOFOVERRUNS 0x288
#define EMAC_RXDMAOVERRUNS 0x28C
/* EMAC DM644x control registers */
#define EMAC_CTRL_EWCTL (0x4)
#define EMAC_CTRL_EWINTTCNT (0x8)
/* EMAC MDIO related */
/* Mask & Control defines */
#define MDIO_CONTROL_CLKDIV (0xFF)
#define MDIO_CONTROL_ENABLE (1 << 30)
#define MDIO_USERACCESS_GO (1 << 31)
#define MDIO_USERACCESS_WRITE (1 << 30)
#define MDIO_USERACCESS_READ (0 << 30)
#define MDIO_USERACCESS_WRITE (1 << 30)
#define MDIO_USERACCESS_REGADR (0x1F << 21)
#define MDIO_USERACCESS_PHYADR (0x1F << 16)
#define MDIO_USERACCESS_DATA (0xFFFF)
#define MDIO_USERPHYSEL_LINKSEL (1 << 7)
#define MDIO_VER_MODID (0xFFFF << 16)
#define MDIO_VER_REVMAJ (0xFF << 8)
#define MDIO_VER_REVMIN (0xFF)
#define MDIO_USERACCESS(inst) (0x80 + (inst * 8))
#define MDIO_USERPHYSEL(inst) (0x84 + (inst * 8))
#define MDIO_CONTROL (0x04)
/* EMAC DM646X control module registers */
#define EMAC_DM646X_CMRXINTEN (0x14)
#define EMAC_DM646X_CMTXINTEN (0x18)
/* EMAC EOI codes for C0 */
#define EMAC_DM646X_MAC_EOI_C0_RXEN (0x01)
#define EMAC_DM646X_MAC_EOI_C0_TXEN (0x02)
/** net_buf_obj: EMAC network bufferdata structure
*
* EMAC network buffer data structure
*/
struct emac_netbufobj {
void *buf_token;
char *data_ptr;
int length;
};
/** net_pkt_obj: EMAC network packet data structure
*
* EMAC network packet data structure - supports buffer list (for future)
*/
struct emac_netpktobj {
void *pkt_token; /* data token may hold tx/rx chan id */
struct emac_netbufobj *buf_list; /* array of network buffer objects */
int num_bufs;
int pkt_length;
};
/** emac_tx_bd: EMAC TX Buffer descriptor data structure
*
* EMAC TX Buffer descriptor data structure
*/
struct emac_tx_bd {
int h_next;
int buff_ptr;
int off_b_len;
int mode; /* SOP, EOP, ownership, EOQ, teardown,Qstarv, length */
struct emac_tx_bd __iomem *next;
void *buf_token;
};
/** emac_txch: EMAC TX Channel data structure
*
* EMAC TX Channel data structure
*/
struct emac_txch {
/* Config related */
u32 num_bd;
u32 service_max;
/* CPPI specific */
u32 alloc_size;
void __iomem *bd_mem;
struct emac_tx_bd __iomem *bd_pool_head;
struct emac_tx_bd __iomem *active_queue_head;
struct emac_tx_bd __iomem *active_queue_tail;
struct emac_tx_bd __iomem *last_hw_bdprocessed;
u32 queue_active;
u32 teardown_pending;
u32 *tx_complete;
/** statistics */
u32 proc_count; /* TX: # of times emac_tx_bdproc is called */
u32 mis_queued_packets;
u32 queue_reinit;
u32 end_of_queue_add;
u32 out_of_tx_bd;
u32 no_active_pkts; /* IRQ when there were no packets to process */
u32 active_queue_count;
};
/** emac_rx_bd: EMAC RX Buffer descriptor data structure
*
* EMAC RX Buffer descriptor data structure
*/
struct emac_rx_bd {
int h_next;
int buff_ptr;
int off_b_len;
int mode;
struct emac_rx_bd __iomem *next;
void *data_ptr;
void *buf_token;
};
/** emac_rxch: EMAC RX Channel data structure
*
* EMAC RX Channel data structure
*/
struct emac_rxch {
/* configuration info */
u32 num_bd;
u32 service_max;
u32 buf_size;
char mac_addr[6];
/** CPPI specific */
u32 alloc_size;
void __iomem *bd_mem;
struct emac_rx_bd __iomem *bd_pool_head;
struct emac_rx_bd __iomem *active_queue_head;
struct emac_rx_bd __iomem *active_queue_tail;
u32 queue_active;
u32 teardown_pending;
/* packet and buffer objects */
struct emac_netpktobj pkt_queue;
struct emac_netbufobj buf_queue;
/** statistics */
u32 proc_count; /* number of times emac_rx_bdproc is called */
u32 processed_bd;
u32 recycled_bd;
u32 out_of_rx_bd;
u32 out_of_rx_buffers;
u32 queue_reinit;
u32 end_of_queue_add;
u32 end_of_queue;
u32 mis_queued_packets;
};
/* emac_priv: EMAC private data structure
*
* EMAC adapter private data structure
*/
struct emac_priv {
u32 msg_enable;
struct net_device *ndev;
struct platform_device *pdev;
struct napi_struct napi;
char mac_addr[6];
spinlock_t tx_lock;
spinlock_t rx_lock;
void __iomem *remap_addr;
u32 emac_base_phys;
void __iomem *emac_base;
void __iomem *ctrl_base;
void __iomem *emac_ctrl_ram;
u32 ctrl_ram_size;
struct emac_txch *txch[EMAC_DEF_MAX_TX_CH];
struct emac_rxch *rxch[EMAC_DEF_MAX_RX_CH];
u32 link; /* 1=link on, 0=link off */
u32 speed; /* 0=Auto Neg, 1=No PHY, 10,100, 1000 - mbps */
u32 duplex; /* Link duplex: 0=Half, 1=Full */
u32 rx_buf_size;
u32 isr_count;
u8 rmii_en;
u8 version;
struct net_device_stats net_dev_stats;
u32 mac_hash1;
u32 mac_hash2;
u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS];
u32 rx_addr_type;
/* periodic timer required for MDIO polling */
struct timer_list periodic_timer;
u32 periodic_ticks;
u32 timer_active;
u32 phy_mask;
/* mii_bus,phy members */
struct mii_bus *mii_bus;
struct phy_device *phydev;
spinlock_t lock;
};
/* clock frequency for EMAC */
static struct clk *emac_clk;
static unsigned long emac_bus_frequency;
static unsigned long mdio_max_freq;
/* EMAC internal utility function */
static inline u32 emac_virt_to_phys(void __iomem *addr)
{
return (u32 __force) io_v2p(addr);
}
/* Cache macros - Packet buffers would be from skb pool which is cached */
#define EMAC_VIRT_NOCACHE(addr) (addr)
#define EMAC_CACHE_INVALIDATE(addr, size) \
dma_cache_maint((void *)addr, size, DMA_FROM_DEVICE)
#define EMAC_CACHE_WRITEBACK(addr, size) \
dma_cache_maint((void *)addr, size, DMA_TO_DEVICE)
#define EMAC_CACHE_WRITEBACK_INVALIDATE(addr, size) \
dma_cache_maint((void *)addr, size, DMA_BIDIRECTIONAL)
/* DM644x does not have BD's in cached memory - so no cache functions */
#define BD_CACHE_INVALIDATE(addr, size)
#define BD_CACHE_WRITEBACK(addr, size)
#define BD_CACHE_WRITEBACK_INVALIDATE(addr, size)
/* EMAC TX Host Error description strings */
static char *emac_txhost_errcodes[16] = {
"No error", "SOP error", "Ownership bit not set in SOP buffer",
"Zero Next Buffer Descriptor Pointer Without EOP",
"Zero Buffer Pointer", "Zero Buffer Length", "Packet Length Error",
"Reserved", "Reserved", "Reserved", "Reserved", "Reserved",
"Reserved", "Reserved", "Reserved", "Reserved"
};
/* EMAC RX Host Error description strings */
static char *emac_rxhost_errcodes[16] = {
"No error", "Reserved", "Ownership bit not set in input buffer",
"Reserved", "Zero Buffer Pointer", "Reserved", "Reserved",
"Reserved", "Reserved", "Reserved", "Reserved", "Reserved",
"Reserved", "Reserved", "Reserved", "Reserved"
};
/* FIXME macros capturing values from the envirnment are dangerous! */
#define EMAC_DEV (&(priv)->ndev->dev)
/* Helper macros */
#define emac_read(reg) ioread32(priv->emac_base + (reg))
#define emac_write(reg, val) iowrite32(val, priv->emac_base + (reg))
#define emac_ctrl_read(reg) ioread32((priv->ctrl_base + (reg)))
#define emac_ctrl_write(reg, val) iowrite32(val, (priv->ctrl_base + (reg)))
#define emac_mdio_read(reg) ioread32(bus->priv + (reg))
#define emac_mdio_write(reg, val) iowrite32(val, (bus->priv + (reg)))
/**
* emac_dump_regs: Dump important EMAC registers to debug terminal
* @priv: The DaVinci EMAC private adapter structure
*
* Executes ethtool set cmd & sets phy mode
*
*/
static void emac_dump_regs(struct emac_priv *priv)
{
/* Print important registers in EMAC */
dev_info(EMAC_DEV, "EMACBasic registers\n");
dev_info(EMAC_DEV, "EMAC: EWCTL: %08X, EWINTTCNT: %08X\n",
emac_ctrl_read(EMAC_CTRL_EWCTL),
emac_ctrl_read(EMAC_CTRL_EWINTTCNT));
dev_info(EMAC_DEV, "EMAC: TXID: %08X %s, RXID: %08X %s\n",
emac_read(EMAC_TXIDVER),
((emac_read(EMAC_TXCONTROL)) ? "enabled" : "disabled"),
emac_read(EMAC_RXIDVER),
((emac_read(EMAC_RXCONTROL)) ? "enabled" : "disabled"));
dev_info(EMAC_DEV, "EMAC: TXIntRaw:%08X, TxIntMasked: %08X, "\
"TxIntMasSet: %08X\n", emac_read(EMAC_TXINTSTATRAW),
emac_read(EMAC_TXINTSTATMASKED), emac_read(EMAC_TXINTMASKSET));
dev_info(EMAC_DEV, "EMAC: RXIntRaw:%08X, RxIntMasked: %08X, "\
"RxIntMasSet: %08X\n", emac_read(EMAC_RXINTSTATRAW),
emac_read(EMAC_RXINTSTATMASKED), emac_read(EMAC_RXINTMASKSET));
dev_info(EMAC_DEV, "EMAC: MacIntRaw:%08X, MacIntMasked: %08X, "\
"MacInVector=%08X\n", emac_read(EMAC_MACINTSTATRAW),
emac_read(EMAC_MACINTSTATMASKED), emac_read(EMAC_MACINVECTOR));
dev_info(EMAC_DEV, "EMAC: EmuControl:%08X, FifoControl: %08X\n",
emac_read(EMAC_EMCONTROL), emac_read(EMAC_FIFOCONTROL));
dev_info(EMAC_DEV, "EMAC: MBPEnable:%08X, RXUnicastSet: %08X, "\
"RXMaxLen=%08X\n", emac_read(EMAC_RXMBPENABLE),
emac_read(EMAC_RXUNICASTSET), emac_read(EMAC_RXMAXLEN));
dev_info(EMAC_DEV, "EMAC: MacControl:%08X, MacStatus: %08X, "\
"MacConfig=%08X\n", emac_read(EMAC_MACCONTROL),
emac_read(EMAC_MACSTATUS), emac_read(EMAC_MACCONFIG));
dev_info(EMAC_DEV, "EMAC: TXHDP[0]:%08X, RXHDP[0]: %08X\n",
emac_read(EMAC_TXHDP(0)), emac_read(EMAC_RXHDP(0)));
dev_info(EMAC_DEV, "EMAC Statistics\n");
dev_info(EMAC_DEV, "EMAC: rx_good_frames:%d\n",
emac_read(EMAC_RXGOODFRAMES));
dev_info(EMAC_DEV, "EMAC: rx_broadcast_frames:%d\n",
emac_read(EMAC_RXBCASTFRAMES));
dev_info(EMAC_DEV, "EMAC: rx_multicast_frames:%d\n",
emac_read(EMAC_RXMCASTFRAMES));
dev_info(EMAC_DEV, "EMAC: rx_pause_frames:%d\n",
emac_read(EMAC_RXPAUSEFRAMES));
dev_info(EMAC_DEV, "EMAC: rx_crcerrors:%d\n",
emac_read(EMAC_RXCRCERRORS));
dev_info(EMAC_DEV, "EMAC: rx_align_code_errors:%d\n",
emac_read(EMAC_RXALIGNCODEERRORS));
dev_info(EMAC_DEV, "EMAC: rx_oversized_frames:%d\n",
emac_read(EMAC_RXOVERSIZED));
dev_info(EMAC_DEV, "EMAC: rx_jabber_frames:%d\n",
emac_read(EMAC_RXJABBER));
dev_info(EMAC_DEV, "EMAC: rx_undersized_frames:%d\n",
emac_read(EMAC_RXUNDERSIZED));
dev_info(EMAC_DEV, "EMAC: rx_fragments:%d\n",
emac_read(EMAC_RXFRAGMENTS));
dev_info(EMAC_DEV, "EMAC: rx_filtered_frames:%d\n",
emac_read(EMAC_RXFILTERED));
dev_info(EMAC_DEV, "EMAC: rx_qos_filtered_frames:%d\n",
emac_read(EMAC_RXQOSFILTERED));
dev_info(EMAC_DEV, "EMAC: rx_octets:%d\n",
emac_read(EMAC_RXOCTETS));
dev_info(EMAC_DEV, "EMAC: tx_goodframes:%d\n",
emac_read(EMAC_TXGOODFRAMES));
dev_info(EMAC_DEV, "EMAC: tx_bcastframes:%d\n",
emac_read(EMAC_TXBCASTFRAMES));
dev_info(EMAC_DEV, "EMAC: tx_mcastframes:%d\n",
emac_read(EMAC_TXMCASTFRAMES));
dev_info(EMAC_DEV, "EMAC: tx_pause_frames:%d\n",
emac_read(EMAC_TXPAUSEFRAMES));
dev_info(EMAC_DEV, "EMAC: tx_deferred_frames:%d\n",
emac_read(EMAC_TXDEFERRED));
dev_info(EMAC_DEV, "EMAC: tx_collision_frames:%d\n",
emac_read(EMAC_TXCOLLISION));
dev_info(EMAC_DEV, "EMAC: tx_single_coll_frames:%d\n",
emac_read(EMAC_TXSINGLECOLL));
dev_info(EMAC_DEV, "EMAC: tx_mult_coll_frames:%d\n",
emac_read(EMAC_TXMULTICOLL));
dev_info(EMAC_DEV, "EMAC: tx_excessive_collisions:%d\n",
emac_read(EMAC_TXEXCESSIVECOLL));
dev_info(EMAC_DEV, "EMAC: tx_late_collisions:%d\n",
emac_read(EMAC_TXLATECOLL));
dev_info(EMAC_DEV, "EMAC: tx_underrun:%d\n",
emac_read(EMAC_TXUNDERRUN));
dev_info(EMAC_DEV, "EMAC: tx_carrier_sense_errors:%d\n",
emac_read(EMAC_TXCARRIERSENSE));
dev_info(EMAC_DEV, "EMAC: tx_octets:%d\n",
emac_read(EMAC_TXOCTETS));
dev_info(EMAC_DEV, "EMAC: net_octets:%d\n",
emac_read(EMAC_NETOCTETS));
dev_info(EMAC_DEV, "EMAC: rx_sof_overruns:%d\n",
emac_read(EMAC_RXSOFOVERRUNS));
dev_info(EMAC_DEV, "EMAC: rx_mof_overruns:%d\n",
emac_read(EMAC_RXMOFOVERRUNS));
dev_info(EMAC_DEV, "EMAC: rx_dma_overruns:%d\n",
emac_read(EMAC_RXDMAOVERRUNS));
dev_info(EMAC_DEV, "\n");
}
/*************************************************************************
* EMAC MDIO/Phy Functionality
*************************************************************************/
/**
* emac_get_drvinfo: Get EMAC driver information
* @ndev: The DaVinci EMAC network adapter
* @info: ethtool info structure containing name and version
*
* Returns EMAC driver information (name and version)
*
*/
static void emac_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *info)
{
strcpy(info->driver, emac_version_string);
strcpy(info->version, EMAC_MODULE_VERSION);
}
/**
* emac_get_settings: Get EMAC settings
* @ndev: The DaVinci EMAC network adapter
* @ecmd: ethtool command
*
* Executes ethool get command
*
*/
static int emac_get_settings(struct net_device *ndev,
struct ethtool_cmd *ecmd)
{
struct emac_priv *priv = netdev_priv(ndev);
if (priv->phy_mask)
return phy_ethtool_gset(priv->phydev, ecmd);
else
return -EOPNOTSUPP;
}
/**
* emac_set_settings: Set EMAC settings
* @ndev: The DaVinci EMAC network adapter
* @ecmd: ethtool command
*
* Executes ethool set command
*
*/
static int emac_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)
{
struct emac_priv *priv = netdev_priv(ndev);
if (priv->phy_mask)
return phy_ethtool_sset(priv->phydev, ecmd);
else
return -EOPNOTSUPP;
}
/**
* ethtool_ops: DaVinci EMAC Ethtool structure
*
* Ethtool support for EMAC adapter
*
*/
static const struct ethtool_ops ethtool_ops = {
.get_drvinfo = emac_get_drvinfo,
.get_settings = emac_get_settings,
.set_settings = emac_set_settings,
.get_link = ethtool_op_get_link,
};
/**
* emac_update_phystatus: Update Phy status
* @priv: The DaVinci EMAC private adapter structure
*
* Updates phy status and takes action for network queue if required
* based upon link status
*
*/
static void emac_update_phystatus(struct emac_priv *priv)
{
u32 mac_control;
u32 new_duplex;
u32 cur_duplex;
struct net_device *ndev = priv->ndev;
mac_control = emac_read(EMAC_MACCONTROL);
cur_duplex = (mac_control & EMAC_MACCONTROL_FULLDUPLEXEN) ?
DUPLEX_FULL : DUPLEX_HALF;
if (priv->phy_mask)
new_duplex = priv->phydev->duplex;
else
new_duplex = DUPLEX_FULL;
/* We get called only if link has changed (speed/duplex/status) */
if ((priv->link) && (new_duplex != cur_duplex)) {
priv->duplex = new_duplex;
if (DUPLEX_FULL == priv->duplex)
mac_control |= (EMAC_MACCONTROL_FULLDUPLEXEN);
else
mac_control &= ~(EMAC_MACCONTROL_FULLDUPLEXEN);
}
if (priv->speed == SPEED_1000 && (priv->version == EMAC_VERSION_2)) {
mac_control = emac_read(EMAC_MACCONTROL);
mac_control |= (EMAC_DM646X_MACCONTORL_GMIIEN |
EMAC_DM646X_MACCONTORL_GIG |
EMAC_DM646X_MACCONTORL_GIGFORCE);
} else {
/* Clear the GIG bit and GIGFORCE bit */
mac_control &= ~(EMAC_DM646X_MACCONTORL_GIGFORCE |
EMAC_DM646X_MACCONTORL_GIG);
if (priv->rmii_en && (priv->speed == SPEED_100))
mac_control |= EMAC_MACCONTROL_RMIISPEED_MASK;
else
mac_control &= ~EMAC_MACCONTROL_RMIISPEED_MASK;
}
/* Update mac_control if changed */
emac_write(EMAC_MACCONTROL, mac_control);
if (priv->link) {
/* link ON */
if (!netif_carrier_ok(ndev))
netif_carrier_on(ndev);
/* reactivate the transmit queue if it is stopped */
if (netif_running(ndev) && netif_queue_stopped(ndev))
netif_wake_queue(ndev);
} else {
/* link OFF */
if (netif_carrier_ok(ndev))
netif_carrier_off(ndev);
if (!netif_queue_stopped(ndev))
netif_stop_queue(ndev);
}
}
/**
* hash_get: Calculate hash value from mac address
* @addr: mac address to delete from hash table
*
* Calculates hash value from mac address
*
*/
static u32 hash_get(u8 *addr)
{
u32 hash;
u8 tmpval;
int cnt;
hash = 0;
for (cnt = 0; cnt < 2; cnt++) {
tmpval = *addr++;
hash ^= (tmpval >> 2) ^ (tmpval << 4);
tmpval = *addr++;
hash ^= (tmpval >> 4) ^ (tmpval << 2);
tmpval = *addr++;
hash ^= (tmpval >> 6) ^ (tmpval);
}
return hash & 0x3F;
}
/**
* hash_add: Hash function to add mac addr from hash table
* @priv: The DaVinci EMAC private adapter structure
* mac_addr: mac address to delete from hash table
*
* Adds mac address to the internal hash table
*
*/
static int hash_add(struct emac_priv *priv, u8 *mac_addr)
{
u32 rc = 0;
u32 hash_bit;
u32 hash_value = hash_get(mac_addr);
if (hash_value >= EMAC_NUM_MULTICAST_BITS) {
if (netif_msg_drv(priv)) {
dev_err(EMAC_DEV, "DaVinci EMAC: hash_add(): Invalid "\
"Hash %08x, should not be greater than %08x",
hash_value, (EMAC_NUM_MULTICAST_BITS - 1));
}
return -1;
}
/* set the hash bit only if not previously set */
if (priv->multicast_hash_cnt[hash_value] == 0) {
rc = 1; /* hash value changed */
if (hash_value < 32) {
hash_bit = (1 << hash_value);
priv->mac_hash1 |= hash_bit;
} else {
hash_bit = (1 << (hash_value - 32));
priv->mac_hash2 |= hash_bit;
}
}
/* incr counter for num of mcast addr's mapped to "this" hash bit */
++priv->multicast_hash_cnt[hash_value];
return rc;
}
/**
* hash_del: Hash function to delete mac addr from hash table
* @priv: The DaVinci EMAC private adapter structure
* mac_addr: mac address to delete from hash table
*
* Removes mac address from the internal hash table
*
*/
static int hash_del(struct emac_priv *priv, u8 *mac_addr)
{
u32 hash_value;
u32 hash_bit;
hash_value = hash_get(mac_addr);
if (priv->multicast_hash_cnt[hash_value] > 0) {
/* dec cntr for num of mcast addr's mapped to this hash bit */
--priv->multicast_hash_cnt[hash_value];
}
/* if counter still > 0, at least one multicast address refers
* to this hash bit. so return 0 */
if (priv->multicast_hash_cnt[hash_value] > 0)
return 0;
if (hash_value < 32) {
hash_bit = (1 << hash_value);
priv->mac_hash1 &= ~hash_bit;
} else {
hash_bit = (1 << (hash_value - 32));
priv->mac_hash2 &= ~hash_bit;
}
/* return 1 to indicate change in mac_hash registers reqd */
return 1;
}
/* EMAC multicast operation */
#define EMAC_MULTICAST_ADD 0
#define EMAC_MULTICAST_DEL 1
#define EMAC_ALL_MULTI_SET 2
#define EMAC_ALL_MULTI_CLR 3
/**
* emac_add_mcast: Set multicast address in the EMAC adapter (Internal)
* @priv: The DaVinci EMAC private adapter structure
* @action: multicast operation to perform
* mac_addr: mac address to set
*
* Set multicast addresses in EMAC adapter - internal function
*
*/
static void emac_add_mcast(struct emac_priv *priv, u32 action, u8 *mac_addr)
{
int update = -1;
switch (action) {
case EMAC_MULTICAST_ADD:
update = hash_add(priv, mac_addr);
break;
case EMAC_MULTICAST_DEL:
update = hash_del(priv, mac_addr);
break;
case EMAC_ALL_MULTI_SET:
update = 1;
priv->mac_hash1 = EMAC_ALL_MULTI_REG_VALUE;
priv->mac_hash2 = EMAC_ALL_MULTI_REG_VALUE;
break;
case EMAC_ALL_MULTI_CLR:
update = 1;
priv->mac_hash1 = 0;
priv->mac_hash2 = 0;
memset(&(priv->multicast_hash_cnt[0]), 0,
sizeof(priv->multicast_hash_cnt[0]) *
EMAC_NUM_MULTICAST_BITS);
break;
default:
if (netif_msg_drv(priv))
dev_err(EMAC_DEV, "DaVinci EMAC: add_mcast"\
": bad operation %d", action);
break;
}
/* write to the hardware only if the register status chances */
if (update > 0) {
emac_write(EMAC_MACHASH1, priv->mac_hash1);
emac_write(EMAC_MACHASH2, priv->mac_hash2);
}
}
/**
* emac_dev_mcast_set: Set multicast address in the EMAC adapter
* @ndev: The DaVinci EMAC network adapter
*
* Set multicast addresses in EMAC adapter
*
*/
static void emac_dev_mcast_set(struct net_device *ndev)
{
u32 mbp_enable;
struct emac_priv *priv = netdev_priv(ndev);
mbp_enable = emac_read(EMAC_RXMBPENABLE);
if (ndev->flags & IFF_PROMISC) {
mbp_enable &= (~EMAC_MBP_PROMISCCH(EMAC_DEF_PROM_CH));
mbp_enable |= (EMAC_MBP_RXPROMISC);
} else {
mbp_enable = (mbp_enable & ~EMAC_MBP_RXPROMISC);
if ((ndev->flags & IFF_ALLMULTI) ||
(ndev->mc_count > EMAC_DEF_MAX_MULTICAST_ADDRESSES)) {
mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST);
emac_add_mcast(priv, EMAC_ALL_MULTI_SET, NULL);
}
if (ndev->mc_count > 0) {
struct dev_mc_list *mc_ptr;
mbp_enable = (mbp_enable | EMAC_MBP_RXMCAST);
emac_add_mcast(priv, EMAC_ALL_MULTI_CLR, NULL);
/* program multicast address list into EMAC hardware */
for (mc_ptr = ndev->mc_list; mc_ptr;
mc_ptr = mc_ptr->next) {
emac_add_mcast(priv, EMAC_MULTICAST_ADD,
(u8 *)mc_ptr->dmi_addr);
}
} else {
mbp_enable = (mbp_enable & ~EMAC_MBP_RXMCAST);
emac_add_mcast(priv, EMAC_ALL_MULTI_CLR, NULL);
}
}
/* Set mbp config register */
emac_write(EMAC_RXMBPENABLE, mbp_enable);
}
/*************************************************************************
* EMAC Hardware manipulation
*************************************************************************/
/**
* emac_int_disable: Disable EMAC module interrupt (from adapter)
* @priv: The DaVinci EMAC private adapter structure
*
* Disable EMAC interrupt on the adapter
*
*/
static void emac_int_disable(struct emac_priv *priv)
{
if (priv->version == EMAC_VERSION_2) {
unsigned long flags;
local_irq_save(flags);
/* Program C0_Int_En to zero to turn off
* interrupts to the CPU */
emac_ctrl_write(EMAC_DM646X_CMRXINTEN, 0x0);
emac_ctrl_write(EMAC_DM646X_CMTXINTEN, 0x0);
/* NOTE: Rx Threshold and Misc interrupts are not disabled */
local_irq_restore(flags);
} else {
/* Set DM644x control registers for interrupt control */
emac_ctrl_write(EMAC_CTRL_EWCTL, 0x0);
}
}
/**
* emac_int_enable: Enable EMAC module interrupt (from adapter)
* @priv: The DaVinci EMAC private adapter structure
*
* Enable EMAC interrupt on the adapter
*
*/
static void emac_int_enable(struct emac_priv *priv)
{
if (priv->version == EMAC_VERSION_2) {
emac_ctrl_write(EMAC_DM646X_CMRXINTEN, 0xff);
emac_ctrl_write(EMAC_DM646X_CMTXINTEN, 0xff);
/* In addition to turning on interrupt Enable, we need
* ack by writing appropriate values to the EOI
* register */
/* NOTE: Rx Threshold and Misc interrupts are not enabled */
/* ack rxen only then a new pulse will be generated */
emac_write(EMAC_DM646X_MACEOIVECTOR,
EMAC_DM646X_MAC_EOI_C0_RXEN);
/* ack txen- only then a new pulse will be generated */
emac_write(EMAC_DM646X_MACEOIVECTOR,
EMAC_DM646X_MAC_EOI_C0_TXEN);
} else {
/* Set DM644x control registers for interrupt control */
emac_ctrl_write(EMAC_CTRL_EWCTL, 0x1);
}
}
/**
* emac_irq: EMAC interrupt handler
* @irq: interrupt number
* @dev_id: EMAC network adapter data structure ptr
*
* EMAC Interrupt handler - we only schedule NAPI and not process any packets
* here. EVen the interrupt status is checked (TX/RX/Err) in NAPI poll function
*
* Returns interrupt handled condition
*/
static irqreturn_t emac_irq(int irq, void *dev_id)
{
struct net_device *ndev = (struct net_device *)dev_id;
struct emac_priv *priv = netdev_priv(ndev);
++priv->isr_count;
if (likely(netif_running(priv->ndev))) {
emac_int_disable(priv);
napi_schedule(&priv->napi);
} else {
/* we are closing down, so dont process anything */
}
return IRQ_HANDLED;
}
/** EMAC on-chip buffer descriptor memory
*
* WARNING: Please note that the on chip memory is used for both TX and RX
* buffer descriptor queues and is equally divided between TX and RX desc's
* If the number of TX or RX descriptors change this memory pointers need
* to be adjusted. If external memory is allocated then these pointers can
* pointer to the memory
*
*/
#define EMAC_TX_BD_MEM(priv) ((priv)->emac_ctrl_ram)
#define EMAC_RX_BD_MEM(priv) ((priv)->emac_ctrl_ram + \
(((priv)->ctrl_ram_size) >> 1))
/**
* emac_init_txch: TX channel initialization
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
*
* Called during device init to setup a TX channel (allocate buffer desc
* create free pool and keep ready for transmission
*
* Returns success(0) or mem alloc failures error code
*/
static int emac_init_txch(struct emac_priv *priv, u32 ch)
{
u32 cnt, bd_size;
void __iomem *mem;
struct emac_tx_bd __iomem *curr_bd;
struct emac_txch *txch = NULL;
txch = kzalloc(sizeof(struct emac_txch), GFP_KERNEL);
if (NULL == txch) {
dev_err(EMAC_DEV, "DaVinci EMAC: TX Ch mem alloc failed");
return -ENOMEM;
}
priv->txch[ch] = txch;
txch->service_max = EMAC_DEF_TX_MAX_SERVICE;
txch->active_queue_head = NULL;
txch->active_queue_tail = NULL;
txch->queue_active = 0;
txch->teardown_pending = 0;
/* allocate memory for TX CPPI channel on a 4 byte boundry */
txch->tx_complete = kzalloc(txch->service_max * sizeof(u32),
GFP_KERNEL);
if (NULL == txch->tx_complete) {
dev_err(EMAC_DEV, "DaVinci EMAC: Tx service mem alloc failed");
kfree(txch);
return -ENOMEM;
}
/* allocate buffer descriptor pool align every BD on four word
* boundry for future requirements */
bd_size = (sizeof(struct emac_tx_bd) + 0xF) & ~0xF;
txch->num_bd = (priv->ctrl_ram_size >> 1) / bd_size;
txch->alloc_size = (((bd_size * txch->num_bd) + 0xF) & ~0xF);
/* alloc TX BD memory */
txch->bd_mem = EMAC_TX_BD_MEM(priv);
__memzero((void __force *)txch->bd_mem, txch->alloc_size);
/* initialize the BD linked list */
mem = (void __force __iomem *)
(((u32 __force) txch->bd_mem + 0xF) & ~0xF);
txch->bd_pool_head = NULL;
for (cnt = 0; cnt < txch->num_bd; cnt++) {
curr_bd = mem + (cnt * bd_size);
curr_bd->next = txch->bd_pool_head;
txch->bd_pool_head = curr_bd;
}
/* reset statistics counters */
txch->out_of_tx_bd = 0;
txch->no_active_pkts = 0;
txch->active_queue_count = 0;
return 0;
}
/**
* emac_cleanup_txch: Book-keep function to clean TX channel resources
* @priv: The DaVinci EMAC private adapter structure
* @ch: TX channel number
*
* Called to clean up TX channel resources
*
*/
static void emac_cleanup_txch(struct emac_priv *priv, u32 ch)
{
struct emac_txch *txch = priv->txch[ch];
if (txch) {
if (txch->bd_mem)
txch->bd_mem = NULL;
kfree(txch->tx_complete);
kfree(txch);
priv->txch[ch] = NULL;
}
}
/**
* emac_net_tx_complete: TX packet completion function
* @priv: The DaVinci EMAC private adapter structure
* @net_data_tokens: packet token - skb pointer
* @num_tokens: number of skb's to free
* @ch: TX channel number
*
* Frees the skb once packet is transmitted
*
*/
static int emac_net_tx_complete(struct emac_priv *priv,
void **net_data_tokens,
int num_tokens, u32 ch)
{
u32 cnt;
if (unlikely(num_tokens && netif_queue_stopped(priv->ndev)))
netif_start_queue(priv->ndev);
for (cnt = 0; cnt < num_tokens; cnt++) {
struct sk_buff *skb = (struct sk_buff *)net_data_tokens[cnt];
if (skb == NULL)
continue;
priv->net_dev_stats.tx_packets++;
priv->net_dev_stats.tx_bytes += skb->len;
dev_kfree_skb_any(skb);
}
return 0;
}
/**
* emac_txch_teardown: TX channel teardown
* @priv: The DaVinci EMAC private adapter structure
* @ch: TX channel number
*
* Called to teardown TX channel
*
*/
static void emac_txch_teardown(struct emac_priv *priv, u32 ch)
{
u32 teardown_cnt = 0xFFFFFFF0; /* Some high value */
struct emac_txch *txch = priv->txch[ch];
struct emac_tx_bd __iomem *curr_bd;
while ((emac_read(EMAC_TXCP(ch)) & EMAC_TEARDOWN_VALUE) !=
EMAC_TEARDOWN_VALUE) {
/* wait till tx teardown complete */
cpu_relax(); /* TODO: check if this helps ... */
--teardown_cnt;
if (0 == teardown_cnt) {
dev_err(EMAC_DEV, "EMAC: TX teardown aborted\n");
break;
}
}
emac_write(EMAC_TXCP(ch), EMAC_TEARDOWN_VALUE);
/* process sent packets and return skb's to upper layer */
if (1 == txch->queue_active) {
curr_bd = txch->active_queue_head;
while (curr_bd != NULL) {
emac_net_tx_complete(priv, (void __force *)
&curr_bd->buf_token, 1, ch);
if (curr_bd != txch->active_queue_tail)
curr_bd = curr_bd->next;
else
break;
}
txch->bd_pool_head = txch->active_queue_head;
txch->active_queue_head =
txch->active_queue_tail = NULL;
}
}
/**
* emac_stop_txch: Stop TX channel operation
* @priv: The DaVinci EMAC private adapter structure
* @ch: TX channel number
*
* Called to stop TX channel operation
*
*/
static void emac_stop_txch(struct emac_priv *priv, u32 ch)
{
struct emac_txch *txch = priv->txch[ch];
if (txch) {
txch->teardown_pending = 1;
emac_write(EMAC_TXTEARDOWN, 0);
emac_txch_teardown(priv, ch);
txch->teardown_pending = 0;
emac_write(EMAC_TXINTMASKCLEAR, (1 << ch));
}
}
/**
* emac_tx_bdproc: TX buffer descriptor (packet) processing
* @priv: The DaVinci EMAC private adapter structure
* @ch: TX channel number to process buffer descriptors for
* @budget: number of packets allowed to process
* @pending: indication to caller that packets are pending to process
*
* Processes TX buffer descriptors after packets are transmitted - checks
* ownership bit on the TX * descriptor and requeues it to free pool & frees
* the SKB buffer. Only "budget" number of packets are processed and
* indication of pending packets provided to the caller
*
* Returns number of packets processed
*/
static int emac_tx_bdproc(struct emac_priv *priv, u32 ch, u32 budget,
u32 *pending)
{
unsigned long flags;
u32 frame_status;
u32 pkts_processed = 0;
u32 tx_complete_cnt = 0;
struct emac_tx_bd __iomem *curr_bd;
struct emac_txch *txch = priv->txch[ch];
u32 *tx_complete_ptr = txch->tx_complete;
*pending = 0;
if (unlikely(1 == txch->teardown_pending)) {
if (netif_msg_tx_err(priv) && net_ratelimit()) {
dev_err(EMAC_DEV, "DaVinci EMAC:emac_tx_bdproc: "\
"teardown pending\n");
}
return 0; /* dont handle any pkt completions */
}
++txch->proc_count;
spin_lock_irqsave(&priv->tx_lock, flags);
curr_bd = txch->active_queue_head;
if (NULL == curr_bd) {
emac_write(EMAC_TXCP(ch),
emac_virt_to_phys(txch->last_hw_bdprocessed));
txch->no_active_pkts++;
spin_unlock_irqrestore(&priv->tx_lock, flags);
return 0;
}
BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
frame_status = curr_bd->mode;
while ((curr_bd) &&
((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) &&
(pkts_processed < budget)) {
emac_write(EMAC_TXCP(ch), emac_virt_to_phys(curr_bd));
txch->active_queue_head = curr_bd->next;
if (frame_status & EMAC_CPPI_EOQ_BIT) {
if (curr_bd->next) { /* misqueued packet */
emac_write(EMAC_TXHDP(ch), curr_bd->h_next);
++txch->mis_queued_packets;
} else {
txch->queue_active = 0; /* end of queue */
}
}
*tx_complete_ptr = (u32) curr_bd->buf_token;
++tx_complete_ptr;
++tx_complete_cnt;
curr_bd->next = txch->bd_pool_head;
txch->bd_pool_head = curr_bd;
--txch->active_queue_count;
pkts_processed++;
txch->last_hw_bdprocessed = curr_bd;
curr_bd = txch->active_queue_head;
if (curr_bd) {
BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
frame_status = curr_bd->mode;
}
} /* end of pkt processing loop */
if ((pkts_processed == budget) &&
((curr_bd) && ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0))) {
*pending = 1;
}
emac_net_tx_complete(priv,
(void *)&txch->tx_complete[0],
tx_complete_cnt, ch);
spin_unlock_irqrestore(&priv->tx_lock, flags);
return pkts_processed;
}
#define EMAC_ERR_TX_OUT_OF_BD -1
/**
* emac_send: EMAC Transmit function (internal)
* @priv: The DaVinci EMAC private adapter structure
* @pkt: packet pointer (contains skb ptr)
* @ch: TX channel number
*
* Called by the transmit function to queue the packet in EMAC hardware queue
*
* Returns success(0) or error code (typically out of desc's)
*/
static int emac_send(struct emac_priv *priv, struct emac_netpktobj *pkt, u32 ch)
{
unsigned long flags;
struct emac_tx_bd __iomem *curr_bd;
struct emac_txch *txch;
struct emac_netbufobj *buf_list;
txch = priv->txch[ch];
buf_list = pkt->buf_list; /* get handle to the buffer array */
/* check packet size and pad if short */
if (pkt->pkt_length < EMAC_DEF_MIN_ETHPKTSIZE) {
buf_list->length += (EMAC_DEF_MIN_ETHPKTSIZE - pkt->pkt_length);
pkt->pkt_length = EMAC_DEF_MIN_ETHPKTSIZE;
}
spin_lock_irqsave(&priv->tx_lock, flags);
curr_bd = txch->bd_pool_head;
if (curr_bd == NULL) {
txch->out_of_tx_bd++;
spin_unlock_irqrestore(&priv->tx_lock, flags);
return EMAC_ERR_TX_OUT_OF_BD;
}
txch->bd_pool_head = curr_bd->next;
curr_bd->buf_token = buf_list->buf_token;
/* FIXME buff_ptr = dma_map_single(... data_ptr ...) */
curr_bd->buff_ptr = virt_to_phys(buf_list->data_ptr);
curr_bd->off_b_len = buf_list->length;
curr_bd->h_next = 0;
curr_bd->next = NULL;
curr_bd->mode = (EMAC_CPPI_SOP_BIT | EMAC_CPPI_OWNERSHIP_BIT |
EMAC_CPPI_EOP_BIT | pkt->pkt_length);
/* flush the packet from cache if write back cache is present */
BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
/* send the packet */
if (txch->active_queue_head == NULL) {
txch->active_queue_head = curr_bd;
txch->active_queue_tail = curr_bd;
if (1 != txch->queue_active) {
emac_write(EMAC_TXHDP(ch),
emac_virt_to_phys(curr_bd));
txch->queue_active = 1;
}
++txch->queue_reinit;
} else {
register struct emac_tx_bd __iomem *tail_bd;
register u32 frame_status;
tail_bd = txch->active_queue_tail;
tail_bd->next = curr_bd;
txch->active_queue_tail = curr_bd;
tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
tail_bd->h_next = (int)emac_virt_to_phys(curr_bd);
frame_status = tail_bd->mode;
if (frame_status & EMAC_CPPI_EOQ_BIT) {
emac_write(EMAC_TXHDP(ch), emac_virt_to_phys(curr_bd));
frame_status &= ~(EMAC_CPPI_EOQ_BIT);
tail_bd->mode = frame_status;
++txch->end_of_queue_add;
}
}
txch->active_queue_count++;
spin_unlock_irqrestore(&priv->tx_lock, flags);
return 0;
}
/**
* emac_dev_xmit: EMAC Transmit function
* @skb: SKB pointer
* @ndev: The DaVinci EMAC network adapter
*
* Called by the system to transmit a packet - we queue the packet in
* EMAC hardware transmit queue
*
* Returns success(NETDEV_TX_OK) or error code (typically out of desc's)
*/
static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)
{
int ret_code;
struct emac_netbufobj tx_buf; /* buffer obj-only single frame support */
struct emac_netpktobj tx_packet; /* packet object */
struct emac_priv *priv = netdev_priv(ndev);
/* If no link, return */
if (unlikely(!priv->link)) {
if (netif_msg_tx_err(priv) && net_ratelimit())
dev_err(EMAC_DEV, "DaVinci EMAC: No link to transmit");
return NETDEV_TX_BUSY;
}
/* Build the buffer and packet objects - Since only single fragment is
* supported, need not set length and token in both packet & object.
* Doing so for completeness sake & to show that this needs to be done
* in multifragment case
*/
tx_packet.buf_list = &tx_buf;
tx_packet.num_bufs = 1; /* only single fragment supported */
tx_packet.pkt_length = skb->len;
tx_packet.pkt_token = (void *)skb;
tx_buf.length = skb->len;
tx_buf.buf_token = (void *)skb;
tx_buf.data_ptr = skb->data;
EMAC_CACHE_WRITEBACK((unsigned long)skb->data, skb->len);
ndev->trans_start = jiffies;
ret_code = emac_send(priv, &tx_packet, EMAC_DEF_TX_CH);
if (unlikely(ret_code != 0)) {
if (ret_code == EMAC_ERR_TX_OUT_OF_BD) {
if (netif_msg_tx_err(priv) && net_ratelimit())
dev_err(EMAC_DEV, "DaVinci EMAC: xmit() fatal"\
" err. Out of TX BD's");
netif_stop_queue(priv->ndev);
}
priv->net_dev_stats.tx_dropped++;
return NETDEV_TX_BUSY;
}
return NETDEV_TX_OK;
}
/**
* emac_dev_tx_timeout: EMAC Transmit timeout function
* @ndev: The DaVinci EMAC network adapter
*
* Called when system detects that a skb timeout period has expired
* potentially due to a fault in the adapter in not being able to send
* it out on the wire. We teardown the TX channel assuming a hardware
* error and re-initialize the TX channel for hardware operation
*
*/
static void emac_dev_tx_timeout(struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
if (netif_msg_tx_err(priv))
dev_err(EMAC_DEV, "DaVinci EMAC: xmit timeout, restarting TX");
priv->net_dev_stats.tx_errors++;
emac_int_disable(priv);
emac_stop_txch(priv, EMAC_DEF_TX_CH);
emac_cleanup_txch(priv, EMAC_DEF_TX_CH);
emac_init_txch(priv, EMAC_DEF_TX_CH);
emac_write(EMAC_TXHDP(0), 0);
emac_write(EMAC_TXINTMASKSET, (1 << EMAC_DEF_TX_CH));
emac_int_enable(priv);
}
/**
* emac_net_alloc_rx_buf: Allocate a skb for RX
* @priv: The DaVinci EMAC private adapter structure
* @buf_size: size of SKB data buffer to allocate
* @data_token: data token returned (skb handle for storing in buffer desc)
* @ch: RX channel number
*
* Called during RX channel setup - allocates skb buffer of required size
* and provides the skb handle and allocated buffer data pointer to caller
*
* Returns skb data pointer or 0 on failure to alloc skb
*/
static void *emac_net_alloc_rx_buf(struct emac_priv *priv, int buf_size,
void **data_token, u32 ch)
{
struct net_device *ndev = priv->ndev;
struct sk_buff *p_skb;
p_skb = dev_alloc_skb(buf_size);
if (unlikely(NULL == p_skb)) {
if (netif_msg_rx_err(priv) && net_ratelimit())
dev_err(EMAC_DEV, "DaVinci EMAC: failed to alloc skb");
return NULL;
}
/* set device pointer in skb and reserve space for extra bytes */
p_skb->dev = ndev;
skb_reserve(p_skb, NET_IP_ALIGN);
*data_token = (void *) p_skb;
EMAC_CACHE_WRITEBACK_INVALIDATE((unsigned long)p_skb->data, buf_size);
return p_skb->data;
}
/**
* emac_init_rxch: RX channel initialization
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
* @param: mac address for RX channel
*
* Called during device init to setup a RX channel (allocate buffers and
* buffer descriptors, create queue and keep ready for reception
*
* Returns success(0) or mem alloc failures error code
*/
static int emac_init_rxch(struct emac_priv *priv, u32 ch, char *param)
{
u32 cnt, bd_size;
void __iomem *mem;
struct emac_rx_bd __iomem *curr_bd;
struct emac_rxch *rxch = NULL;
rxch = kzalloc(sizeof(struct emac_rxch), GFP_KERNEL);
if (NULL == rxch) {
dev_err(EMAC_DEV, "DaVinci EMAC: RX Ch mem alloc failed");
return -ENOMEM;
}
priv->rxch[ch] = rxch;
rxch->buf_size = priv->rx_buf_size;
rxch->service_max = EMAC_DEF_RX_MAX_SERVICE;
rxch->queue_active = 0;
rxch->teardown_pending = 0;
/* save mac address */
for (cnt = 0; cnt < 6; cnt++)
rxch->mac_addr[cnt] = param[cnt];
/* allocate buffer descriptor pool align every BD on four word
* boundry for future requirements */
bd_size = (sizeof(struct emac_rx_bd) + 0xF) & ~0xF;
rxch->num_bd = (priv->ctrl_ram_size >> 1) / bd_size;
rxch->alloc_size = (((bd_size * rxch->num_bd) + 0xF) & ~0xF);
rxch->bd_mem = EMAC_RX_BD_MEM(priv);
__memzero((void __force *)rxch->bd_mem, rxch->alloc_size);
rxch->pkt_queue.buf_list = &rxch->buf_queue;
/* allocate RX buffer and initialize the BD linked list */
mem = (void __force __iomem *)
(((u32 __force) rxch->bd_mem + 0xF) & ~0xF);
rxch->active_queue_head = NULL;
rxch->active_queue_tail = mem;
for (cnt = 0; cnt < rxch->num_bd; cnt++) {
curr_bd = mem + (cnt * bd_size);
/* for future use the last parameter contains the BD ptr */
curr_bd->data_ptr = emac_net_alloc_rx_buf(priv,
rxch->buf_size,
(void __force **)&curr_bd->buf_token,
EMAC_DEF_RX_CH);
if (curr_bd->data_ptr == NULL) {
dev_err(EMAC_DEV, "DaVinci EMAC: RX buf mem alloc " \
"failed for ch %d\n", ch);
kfree(rxch);
return -ENOMEM;
}
/* populate the hardware descriptor */
curr_bd->h_next = emac_virt_to_phys(rxch->active_queue_head);
/* FIXME buff_ptr = dma_map_single(... data_ptr ...) */
curr_bd->buff_ptr = virt_to_phys(curr_bd->data_ptr);
curr_bd->off_b_len = rxch->buf_size;
curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
/* write back to hardware memory */
BD_CACHE_WRITEBACK_INVALIDATE((u32) curr_bd,
EMAC_BD_LENGTH_FOR_CACHE);
curr_bd->next = rxch->active_queue_head;
rxch->active_queue_head = curr_bd;
}
/* At this point rxCppi->activeQueueHead points to the first
RX BD ready to be given to RX HDP and rxch->active_queue_tail
points to the last RX BD
*/
return 0;
}
/**
* emac_rxch_teardown: RX channel teardown
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
*
* Called during device stop to teardown RX channel
*
*/
static void emac_rxch_teardown(struct emac_priv *priv, u32 ch)
{
u32 teardown_cnt = 0xFFFFFFF0; /* Some high value */
while ((emac_read(EMAC_RXCP(ch)) & EMAC_TEARDOWN_VALUE) !=
EMAC_TEARDOWN_VALUE) {
/* wait till tx teardown complete */
cpu_relax(); /* TODO: check if this helps ... */
--teardown_cnt;
if (0 == teardown_cnt) {
dev_err(EMAC_DEV, "EMAC: RX teardown aborted\n");
break;
}
}
emac_write(EMAC_RXCP(ch), EMAC_TEARDOWN_VALUE);
}
/**
* emac_stop_rxch: Stop RX channel operation
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
*
* Called during device stop to stop RX channel operation
*
*/
static void emac_stop_rxch(struct emac_priv *priv, u32 ch)
{
struct emac_rxch *rxch = priv->rxch[ch];
if (rxch) {
rxch->teardown_pending = 1;
emac_write(EMAC_RXTEARDOWN, ch);
/* wait for teardown complete */
emac_rxch_teardown(priv, ch);
rxch->teardown_pending = 0;
emac_write(EMAC_RXINTMASKCLEAR, (1 << ch));
}
}
/**
* emac_cleanup_rxch: Book-keep function to clean RX channel resources
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
*
* Called during device stop to clean up RX channel resources
*
*/
static void emac_cleanup_rxch(struct emac_priv *priv, u32 ch)
{
struct emac_rxch *rxch = priv->rxch[ch];
struct emac_rx_bd __iomem *curr_bd;
if (rxch) {
/* free the receive buffers previously allocated */
curr_bd = rxch->active_queue_head;
while (curr_bd) {
if (curr_bd->buf_token) {
dev_kfree_skb_any((struct sk_buff *)\
curr_bd->buf_token);
}
curr_bd = curr_bd->next;
}
if (rxch->bd_mem)
rxch->bd_mem = NULL;
kfree(rxch);
priv->rxch[ch] = NULL;
}
}
/**
* emac_set_type0addr: Set EMAC Type0 mac address
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
* @mac_addr: MAC address to set in device
*
* Called internally to set Type0 mac address of the adapter (Device)
*
* Returns success (0) or appropriate error code (none as of now)
*/
static void emac_set_type0addr(struct emac_priv *priv, u32 ch, char *mac_addr)
{
u32 val;
val = ((mac_addr[5] << 8) | (mac_addr[4]));
emac_write(EMAC_MACSRCADDRLO, val);
val = ((mac_addr[3] << 24) | (mac_addr[2] << 16) | \
(mac_addr[1] << 8) | (mac_addr[0]));
emac_write(EMAC_MACSRCADDRHI, val);
val = emac_read(EMAC_RXUNICASTSET);
val |= (1 << ch);
emac_write(EMAC_RXUNICASTSET, val);
val = emac_read(EMAC_RXUNICASTCLEAR);
val &= ~(1 << ch);
emac_write(EMAC_RXUNICASTCLEAR, val);
}
/**
* emac_set_type1addr: Set EMAC Type1 mac address
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
* @mac_addr: MAC address to set in device
*
* Called internally to set Type1 mac address of the adapter (Device)
*
* Returns success (0) or appropriate error code (none as of now)
*/
static void emac_set_type1addr(struct emac_priv *priv, u32 ch, char *mac_addr)
{
u32 val;
emac_write(EMAC_MACINDEX, ch);
val = ((mac_addr[5] << 8) | mac_addr[4]);
emac_write(EMAC_MACADDRLO, val);
val = ((mac_addr[3] << 24) | (mac_addr[2] << 16) | \
(mac_addr[1] << 8) | (mac_addr[0]));
emac_write(EMAC_MACADDRHI, val);
emac_set_type0addr(priv, ch, mac_addr);
}
/**
* emac_set_type2addr: Set EMAC Type2 mac address
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
* @mac_addr: MAC address to set in device
* @index: index into RX address entries
* @match: match parameter for RX address matching logic
*
* Called internally to set Type2 mac address of the adapter (Device)
*
* Returns success (0) or appropriate error code (none as of now)
*/
static void emac_set_type2addr(struct emac_priv *priv, u32 ch,
char *mac_addr, int index, int match)
{
u32 val;
emac_write(EMAC_MACINDEX, index);
val = ((mac_addr[3] << 24) | (mac_addr[2] << 16) | \
(mac_addr[1] << 8) | (mac_addr[0]));
emac_write(EMAC_MACADDRHI, val);
val = ((mac_addr[5] << 8) | mac_addr[4] | ((ch & 0x7) << 16) | \
(match << 19) | (1 << 20));
emac_write(EMAC_MACADDRLO, val);
emac_set_type0addr(priv, ch, mac_addr);
}
/**
* emac_setmac: Set mac address in the adapter (internal function)
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number
* @mac_addr: MAC address to set in device
*
* Called internally to set the mac address of the adapter (Device)
*
* Returns success (0) or appropriate error code (none as of now)
*/
static void emac_setmac(struct emac_priv *priv, u32 ch, char *mac_addr)
{
if (priv->rx_addr_type == 0) {
emac_set_type0addr(priv, ch, mac_addr);
} else if (priv->rx_addr_type == 1) {
u32 cnt;
for (cnt = 0; cnt < EMAC_MAX_TXRX_CHANNELS; cnt++)
emac_set_type1addr(priv, ch, mac_addr);
} else if (priv->rx_addr_type == 2) {
emac_set_type2addr(priv, ch, mac_addr, ch, 1);
emac_set_type0addr(priv, ch, mac_addr);
} else {
if (netif_msg_drv(priv))
dev_err(EMAC_DEV, "DaVinci EMAC: Wrong addressing\n");
}
}
/**
* emac_dev_setmac_addr: Set mac address in the adapter
* @ndev: The DaVinci EMAC network adapter
* @addr: MAC address to set in device
*
* Called by the system to set the mac address of the adapter (Device)
*
* Returns success (0) or appropriate error code (none as of now)
*/
static int emac_dev_setmac_addr(struct net_device *ndev, void *addr)
{
struct emac_priv *priv = netdev_priv(ndev);
struct emac_rxch *rxch = priv->rxch[EMAC_DEF_RX_CH];
struct sockaddr *sa = addr;
DECLARE_MAC_BUF(mac);
/* Store mac addr in priv and rx channel and set it in EMAC hw */
memcpy(priv->mac_addr, sa->sa_data, ndev->addr_len);
memcpy(rxch->mac_addr, sa->sa_data, ndev->addr_len);
memcpy(ndev->dev_addr, sa->sa_data, ndev->addr_len);
emac_setmac(priv, EMAC_DEF_RX_CH, rxch->mac_addr);
if (netif_msg_drv(priv))
dev_notice(EMAC_DEV, "DaVinci EMAC: emac_dev_setmac_addr %s\n",
print_mac(mac, priv->mac_addr));
return 0;
}
/**
* emac_addbd_to_rx_queue: Recycle RX buffer descriptor
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number to process buffer descriptors for
* @curr_bd: current buffer descriptor
* @buffer: buffer pointer for descriptor
* @buf_token: buffer token (stores skb information)
*
* Prepares the recycled buffer descriptor and addes it to hardware
* receive queue - if queue empty this descriptor becomes the head
* else addes the descriptor to end of queue
*
*/
static void emac_addbd_to_rx_queue(struct emac_priv *priv, u32 ch,
struct emac_rx_bd __iomem *curr_bd,
char *buffer, void *buf_token)
{
struct emac_rxch *rxch = priv->rxch[ch];
/* populate the hardware descriptor */
curr_bd->h_next = 0;
/* FIXME buff_ptr = dma_map_single(... buffer ...) */
curr_bd->buff_ptr = virt_to_phys(buffer);
curr_bd->off_b_len = rxch->buf_size;
curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
curr_bd->next = NULL;
curr_bd->data_ptr = buffer;
curr_bd->buf_token = buf_token;
/* write back */
BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
if (rxch->active_queue_head == NULL) {
rxch->active_queue_head = curr_bd;
rxch->active_queue_tail = curr_bd;
if (0 != rxch->queue_active) {
emac_write(EMAC_RXHDP(ch),
emac_virt_to_phys(rxch->active_queue_head));
rxch->queue_active = 1;
}
} else {
struct emac_rx_bd __iomem *tail_bd;
u32 frame_status;
tail_bd = rxch->active_queue_tail;
rxch->active_queue_tail = curr_bd;
tail_bd->next = curr_bd;
tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
tail_bd->h_next = emac_virt_to_phys(curr_bd);
frame_status = tail_bd->mode;
if (frame_status & EMAC_CPPI_EOQ_BIT) {
emac_write(EMAC_RXHDP(ch),
emac_virt_to_phys(curr_bd));
frame_status &= ~(EMAC_CPPI_EOQ_BIT);
tail_bd->mode = frame_status;
++rxch->end_of_queue_add;
}
}
++rxch->recycled_bd;
}
/**
* emac_net_rx_cb: Prepares packet and sends to upper layer
* @priv: The DaVinci EMAC private adapter structure
* @net_pkt_list: Network packet list (received packets)
*
* Invalidates packet buffer memory and sends the received packet to upper
* layer
*
* Returns success or appropriate error code (none as of now)
*/
static int emac_net_rx_cb(struct emac_priv *priv,
struct emac_netpktobj *net_pkt_list)
{
struct sk_buff *p_skb;
p_skb = (struct sk_buff *)net_pkt_list->pkt_token;
/* set length of packet */
skb_put(p_skb, net_pkt_list->pkt_length);
EMAC_CACHE_INVALIDATE((unsigned long)p_skb->data, p_skb->len);
p_skb->protocol = eth_type_trans(p_skb, priv->ndev);
p_skb->dev->last_rx = jiffies;
netif_receive_skb(p_skb);
priv->net_dev_stats.rx_bytes += net_pkt_list->pkt_length;
priv->net_dev_stats.rx_packets++;
return 0;
}
/**
* emac_rx_bdproc: RX buffer descriptor (packet) processing
* @priv: The DaVinci EMAC private adapter structure
* @ch: RX channel number to process buffer descriptors for
* @budget: number of packets allowed to process
* @pending: indication to caller that packets are pending to process
*
* Processes RX buffer descriptors - checks ownership bit on the RX buffer
* descriptor, sends the receive packet to upper layer, allocates a new SKB
* and recycles the buffer descriptor (requeues it in hardware RX queue).
* Only "budget" number of packets are processed and indication of pending
* packets provided to the caller.
*
* Returns number of packets processed (and indication of pending packets)
*/
static int emac_rx_bdproc(struct emac_priv *priv, u32 ch, u32 budget,
u32 *pending)
{
unsigned long flags;
u32 frame_status;
u32 pkts_processed = 0;
char *new_buffer;
struct emac_rx_bd __iomem *curr_bd;
struct emac_rx_bd __iomem *last_bd;
struct emac_netpktobj *curr_pkt, pkt_obj;
struct emac_netbufobj buf_obj;
struct emac_netbufobj *rx_buf_obj;
void *new_buf_token;
struct emac_rxch *rxch = priv->rxch[ch];
*pending = 0;
if (unlikely(1 == rxch->teardown_pending))
return 0;
++rxch->proc_count;
spin_lock_irqsave(&priv->rx_lock, flags);
pkt_obj.buf_list = &buf_obj;
curr_pkt = &pkt_obj;
curr_bd = rxch->active_queue_head;
BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
frame_status = curr_bd->mode;
while ((curr_bd) &&
((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) &&
(pkts_processed < budget)) {
new_buffer = emac_net_alloc_rx_buf(priv, rxch->buf_size,
&new_buf_token, EMAC_DEF_RX_CH);
if (unlikely(NULL == new_buffer)) {
++rxch->out_of_rx_buffers;
goto end_emac_rx_bdproc;
}
/* populate received packet data structure */
rx_buf_obj = &curr_pkt->buf_list[0];
rx_buf_obj->data_ptr = (char *)curr_bd->data_ptr;
rx_buf_obj->length = curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE;
rx_buf_obj->buf_token = curr_bd->buf_token;
curr_pkt->pkt_token = curr_pkt->buf_list->buf_token;
curr_pkt->num_bufs = 1;
curr_pkt->pkt_length =
(frame_status & EMAC_RX_BD_PKT_LENGTH_MASK);
emac_write(EMAC_RXCP(ch), emac_virt_to_phys(curr_bd));
++rxch->processed_bd;
last_bd = curr_bd;
curr_bd = last_bd->next;
rxch->active_queue_head = curr_bd;
/* check if end of RX queue ? */
if (frame_status & EMAC_CPPI_EOQ_BIT) {
if (curr_bd) {
++rxch->mis_queued_packets;
emac_write(EMAC_RXHDP(ch),
emac_virt_to_phys(curr_bd));
} else {
++rxch->end_of_queue;
rxch->queue_active = 0;
}
}
/* recycle BD */
emac_addbd_to_rx_queue(priv, ch, last_bd, new_buffer,
new_buf_token);
/* return the packet to the user - BD ptr passed in
* last parameter for potential *future* use */
spin_unlock_irqrestore(&priv->rx_lock, flags);
emac_net_rx_cb(priv, curr_pkt);
spin_lock_irqsave(&priv->rx_lock, flags);
curr_bd = rxch->active_queue_head;
if (curr_bd) {
BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
frame_status = curr_bd->mode;
}
++pkts_processed;
}
if ((pkts_processed == budget) &&
((curr_bd) && ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0))) {
*pending = 1;
}
end_emac_rx_bdproc:
spin_unlock_irqrestore(&priv->rx_lock, flags);
return pkts_processed;
}
/**
* emac_hw_enable: Enable EMAC hardware for packet transmission/reception
* @priv: The DaVinci EMAC private adapter structure
*
* Enables EMAC hardware for packet processing - enables PHY, enables RX
* for packet reception and enables device interrupts and then NAPI
*
* Returns success (0) or appropriate error code (none right now)
*/
static int emac_hw_enable(struct emac_priv *priv)
{
u32 ch, val, mbp_enable, mac_control;
/* Soft reset */
emac_write(EMAC_SOFTRESET, 1);
while (emac_read(EMAC_SOFTRESET))
cpu_relax();
/* Disable interrupt & Set pacing for more interrupts initially */
emac_int_disable(priv);
/* Full duplex enable bit set when auto negotiation happens */
mac_control =
(((EMAC_DEF_TXPRIO_FIXED) ? (EMAC_MACCONTROL_TXPTYPE) : 0x0) |
((priv->speed == 1000) ? EMAC_MACCONTROL_GIGABITEN : 0x0) |
((EMAC_DEF_TXPACING_EN) ? (EMAC_MACCONTROL_TXPACEEN) : 0x0) |
((priv->duplex == DUPLEX_FULL) ? 0x1 : 0));
emac_write(EMAC_MACCONTROL, mac_control);
mbp_enable =
(((EMAC_DEF_PASS_CRC) ? (EMAC_RXMBP_PASSCRC_MASK) : 0x0) |
((EMAC_DEF_QOS_EN) ? (EMAC_RXMBP_QOSEN_MASK) : 0x0) |
((EMAC_DEF_NO_BUFF_CHAIN) ? (EMAC_RXMBP_NOCHAIN_MASK) : 0x0) |
((EMAC_DEF_MACCTRL_FRAME_EN) ? (EMAC_RXMBP_CMFEN_MASK) : 0x0) |
((EMAC_DEF_SHORT_FRAME_EN) ? (EMAC_RXMBP_CSFEN_MASK) : 0x0) |
((EMAC_DEF_ERROR_FRAME_EN) ? (EMAC_RXMBP_CEFEN_MASK) : 0x0) |
((EMAC_DEF_PROM_EN) ? (EMAC_RXMBP_CAFEN_MASK) : 0x0) |
((EMAC_DEF_PROM_CH & EMAC_RXMBP_CHMASK) << \
EMAC_RXMBP_PROMCH_SHIFT) |
((EMAC_DEF_BCAST_EN) ? (EMAC_RXMBP_BROADEN_MASK) : 0x0) |
((EMAC_DEF_BCAST_CH & EMAC_RXMBP_CHMASK) << \
EMAC_RXMBP_BROADCH_SHIFT) |
((EMAC_DEF_MCAST_EN) ? (EMAC_RXMBP_MULTIEN_MASK) : 0x0) |
((EMAC_DEF_MCAST_CH & EMAC_RXMBP_CHMASK) << \
EMAC_RXMBP_MULTICH_SHIFT));
emac_write(EMAC_RXMBPENABLE, mbp_enable);
emac_write(EMAC_RXMAXLEN, (EMAC_DEF_MAX_FRAME_SIZE &
EMAC_RX_MAX_LEN_MASK));
emac_write(EMAC_RXBUFFEROFFSET, (EMAC_DEF_BUFFER_OFFSET &
EMAC_RX_BUFFER_OFFSET_MASK));
emac_write(EMAC_RXFILTERLOWTHRESH, 0);
emac_write(EMAC_RXUNICASTCLEAR, EMAC_RX_UNICAST_CLEAR_ALL);
priv->rx_addr_type = (emac_read(EMAC_MACCONFIG) >> 8) & 0xFF;
val = emac_read(EMAC_TXCONTROL);
val |= EMAC_TX_CONTROL_TX_ENABLE_VAL;
emac_write(EMAC_TXCONTROL, val);
val = emac_read(EMAC_RXCONTROL);
val |= EMAC_RX_CONTROL_RX_ENABLE_VAL;
emac_write(EMAC_RXCONTROL, val);
emac_write(EMAC_MACINTMASKSET, EMAC_MAC_HOST_ERR_INTMASK_VAL);
for (ch = 0; ch < EMAC_DEF_MAX_TX_CH; ch++) {
emac_write(EMAC_TXHDP(ch), 0);
emac_write(EMAC_TXINTMASKSET, (1 << ch));
}
for (ch = 0; ch < EMAC_DEF_MAX_RX_CH; ch++) {
struct emac_rxch *rxch = priv->rxch[ch];
emac_setmac(priv, ch, rxch->mac_addr);
emac_write(EMAC_RXINTMASKSET, (1 << ch));
rxch->queue_active = 1;
emac_write(EMAC_RXHDP(ch),
emac_virt_to_phys(rxch->active_queue_head));
}
/* Enable MII */
val = emac_read(EMAC_MACCONTROL);
val |= (EMAC_MACCONTROL_MIIEN);
emac_write(EMAC_MACCONTROL, val);
/* Enable NAPI and interrupts */
napi_enable(&priv->napi);
emac_int_enable(priv);
return 0;
}
/**
* emac_poll: EMAC NAPI Poll function
* @ndev: The DaVinci EMAC network adapter
* @budget: Number of receive packets to process (as told by NAPI layer)
*
* NAPI Poll function implemented to process packets as per budget. We check
* the type of interrupt on the device and accordingly call the TX or RX
* packet processing functions. We follow the budget for RX processing and
* also put a cap on number of TX pkts processed through config param. The
* NAPI schedule function is called if more packets pending.
*
* Returns number of packets received (in most cases; else TX pkts - rarely)
*/
static int emac_poll(struct napi_struct *napi, int budget)
{
unsigned int mask;
struct emac_priv *priv = container_of(napi, struct emac_priv, napi);
struct net_device *ndev = priv->ndev;
u32 status = 0;
u32 num_pkts = 0;
u32 txpending = 0;
u32 rxpending = 0;
if (!netif_running(ndev))
return 0;
/* Check interrupt vectors and call packet processing */
status = emac_read(EMAC_MACINVECTOR);
mask = EMAC_DM644X_MAC_IN_VECTOR_TX_INT_VEC;
if (priv->version == EMAC_VERSION_2)
mask = EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC;
if (status & mask) {
num_pkts = emac_tx_bdproc(priv, EMAC_DEF_TX_CH,
EMAC_DEF_TX_MAX_SERVICE,
&txpending);
} /* TX processing */
mask = EMAC_DM644X_MAC_IN_VECTOR_RX_INT_VEC;
if (priv->version == EMAC_VERSION_2)
mask = EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC;
if (status & mask) {
num_pkts = emac_rx_bdproc(priv, EMAC_DEF_RX_CH,
budget, &rxpending);
} /* RX processing */
if (txpending || rxpending) {
if (likely(napi_schedule_prep(&priv->napi))) {
emac_int_disable(priv);
__napi_schedule(&priv->napi);
}
} else {
napi_complete(napi);
emac_int_enable(priv);
}
if (unlikely(status & EMAC_DM644X_MAC_IN_VECTOR_HOST_INT)) {
u32 ch, cause;
dev_err(EMAC_DEV, "DaVinci EMAC: Fatal Hardware Error\n");
netif_stop_queue(ndev);
napi_disable(&priv->napi);
status = emac_read(EMAC_MACSTATUS);
cause = ((status & EMAC_MACSTATUS_TXERRCODE_MASK) >>
EMAC_MACSTATUS_TXERRCODE_SHIFT);
if (cause) {
ch = ((status & EMAC_MACSTATUS_TXERRCH_MASK) >>
EMAC_MACSTATUS_TXERRCH_SHIFT);
if (net_ratelimit()) {
dev_err(EMAC_DEV, "TX Host error %s on ch=%d\n",
&emac_txhost_errcodes[cause][0], ch);
}
}
cause = ((status & EMAC_MACSTATUS_RXERRCODE_MASK) >>
EMAC_MACSTATUS_RXERRCODE_SHIFT);
if (cause) {
ch = ((status & EMAC_MACSTATUS_RXERRCH_MASK) >>
EMAC_MACSTATUS_RXERRCH_SHIFT);
if (netif_msg_hw(priv) && net_ratelimit())
dev_err(EMAC_DEV, "RX Host error %s on ch=%d\n",
&emac_rxhost_errcodes[cause][0], ch);
}
} /* Host error processing */
return num_pkts;
}
#ifdef CONFIG_NET_POLL_CONTROLLER
/**
* emac_poll_controller: EMAC Poll controller function
* @ndev: The DaVinci EMAC network adapter
*
* Polled functionality used by netconsole and others in non interrupt mode
*
*/
void emac_poll_controller(struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
emac_int_disable(priv);
emac_irq(ndev->irq, priv);
emac_int_enable(priv);
}
#endif
/* PHY/MII bus related */
/* Wait until mdio is ready for next command */
#define MDIO_WAIT_FOR_USER_ACCESS\
while ((emac_mdio_read((MDIO_USERACCESS(0))) &\
MDIO_USERACCESS_GO) != 0)
static int emac_mii_read(struct mii_bus *bus, int phy_id, int phy_reg)
{
unsigned int phy_data = 0;
unsigned int phy_control;
/* Wait until mdio is ready for next command */
MDIO_WAIT_FOR_USER_ACCESS;
phy_control = (MDIO_USERACCESS_GO |
MDIO_USERACCESS_READ |
((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
((phy_id << 16) & MDIO_USERACCESS_PHYADR) |
(phy_data & MDIO_USERACCESS_DATA));
emac_mdio_write(MDIO_USERACCESS(0), phy_control);
/* Wait until mdio is ready for next command */
MDIO_WAIT_FOR_USER_ACCESS;
return emac_mdio_read(MDIO_USERACCESS(0)) & MDIO_USERACCESS_DATA;
}
static int emac_mii_write(struct mii_bus *bus, int phy_id,
int phy_reg, u16 phy_data)
{
unsigned int control;
/* until mdio is ready for next command */
MDIO_WAIT_FOR_USER_ACCESS;
control = (MDIO_USERACCESS_GO |
MDIO_USERACCESS_WRITE |
((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
((phy_id << 16) & MDIO_USERACCESS_PHYADR) |
(phy_data & MDIO_USERACCESS_DATA));
emac_mdio_write(MDIO_USERACCESS(0), control);
return 0;
}
static int emac_mii_reset(struct mii_bus *bus)
{
unsigned int clk_div;
int mdio_bus_freq = emac_bus_frequency;
if (mdio_max_freq & mdio_bus_freq)
clk_div = ((mdio_bus_freq / mdio_max_freq) - 1);
else
clk_div = 0xFF;
clk_div &= MDIO_CONTROL_CLKDIV;
/* Set enable and clock divider in MDIOControl */
emac_mdio_write(MDIO_CONTROL, (clk_div | MDIO_CONTROL_ENABLE));
return 0;
}
static int mii_irqs[PHY_MAX_ADDR] = { PHY_POLL, PHY_POLL };
/* emac_driver: EMAC MII bus structure */
static struct mii_bus *emac_mii;
static void emac_adjust_link(struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
struct phy_device *phydev = priv->phydev;
unsigned long flags;
int new_state = 0;
spin_lock_irqsave(&priv->lock, flags);
if (phydev->link) {
/* check the mode of operation - full/half duplex */
if (phydev->duplex != priv->duplex) {
new_state = 1;
priv->duplex = phydev->duplex;
}
if (phydev->speed != priv->speed) {
new_state = 1;
priv->speed = phydev->speed;
}
if (!priv->link) {
new_state = 1;
priv->link = 1;
}
} else if (priv->link) {
new_state = 1;
priv->link = 0;
priv->speed = 0;
priv->duplex = ~0;
}
if (new_state) {
emac_update_phystatus(priv);
phy_print_status(priv->phydev);
}
spin_unlock_irqrestore(&priv->lock, flags);
}
/*************************************************************************
* Linux Driver Model
*************************************************************************/
/**
* emac_devioctl: EMAC adapter ioctl
* @ndev: The DaVinci EMAC network adapter
* @ifrq: request parameter
* @cmd: command parameter
*
* EMAC driver ioctl function
*
* Returns success(0) or appropriate error code
*/
static int emac_devioctl(struct net_device *ndev, struct ifreq *ifrq, int cmd)
{
dev_warn(&ndev->dev, "DaVinci EMAC: ioctl not supported\n");
if (!(netif_running(ndev)))
return -EINVAL;
/* TODO: Add phy read and write and private statistics get feature */
return -EOPNOTSUPP;
}
/**
* emac_dev_open: EMAC device open
* @ndev: The DaVinci EMAC network adapter
*
* Called when system wants to start the interface. We init TX/RX channels
* and enable the hardware for packet reception/transmission and start the
* network queue.
*
* Returns 0 for a successful open, or appropriate error code
*/
static int emac_dev_open(struct net_device *ndev)
{
u32 rc, cnt, ch;
int phy_addr;
struct resource *res;
int q, m;
int i = 0;
int k = 0;
struct emac_priv *priv = netdev_priv(ndev);
netif_carrier_off(ndev);
for (cnt = 0; cnt <= ETH_ALEN; cnt++)
ndev->dev_addr[cnt] = priv->mac_addr[cnt];
/* Configuration items */
priv->rx_buf_size = EMAC_DEF_MAX_FRAME_SIZE + NET_IP_ALIGN;
/* Clear basic hardware */
for (ch = 0; ch < EMAC_MAX_TXRX_CHANNELS; ch++) {
emac_write(EMAC_TXHDP(ch), 0);
emac_write(EMAC_RXHDP(ch), 0);
emac_write(EMAC_RXHDP(ch), 0);
emac_write(EMAC_RXINTMASKCLEAR, EMAC_INT_MASK_CLEAR);
emac_write(EMAC_TXINTMASKCLEAR, EMAC_INT_MASK_CLEAR);
}
priv->mac_hash1 = 0;
priv->mac_hash2 = 0;
emac_write(EMAC_MACHASH1, 0);
emac_write(EMAC_MACHASH2, 0);
/* multi ch not supported - open 1 TX, 1RX ch by default */
rc = emac_init_txch(priv, EMAC_DEF_TX_CH);
if (0 != rc) {
dev_err(EMAC_DEV, "DaVinci EMAC: emac_init_txch() failed");
return rc;
}
rc = emac_init_rxch(priv, EMAC_DEF_RX_CH, priv->mac_addr);
if (0 != rc) {
dev_err(EMAC_DEV, "DaVinci EMAC: emac_init_rxch() failed");
return rc;
}
/* Request IRQ */
while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) {
for (i = res->start; i <= res->end; i++) {
if (request_irq(i, emac_irq, IRQF_DISABLED,
ndev->name, ndev))
goto rollback;
}
k++;
}
/* Start/Enable EMAC hardware */
emac_hw_enable(priv);
/* find the first phy */
priv->phydev = NULL;
if (priv->phy_mask) {
for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
if (priv->mii_bus->phy_map[phy_addr]) {
priv->phydev = priv->mii_bus->phy_map[phy_addr];
break;
}
}
if (!priv->phydev) {
printk(KERN_ERR "%s: no PHY found\n", ndev->name);
return -1;
}
priv->phydev = phy_connect(ndev, dev_name(&priv->phydev->dev),
&emac_adjust_link, 0, PHY_INTERFACE_MODE_MII);
if (IS_ERR(priv->phydev)) {
printk(KERN_ERR "%s: Could not attach to PHY\n",
ndev->name);
return PTR_ERR(priv->phydev);
}
priv->link = 0;
priv->speed = 0;
priv->duplex = ~0;
printk(KERN_INFO "%s: attached PHY driver [%s] "
"(mii_bus:phy_addr=%s, id=%x)\n", ndev->name,
priv->phydev->drv->name, dev_name(&priv->phydev->dev),
priv->phydev->phy_id);
} else{
/* No PHY , fix the link, speed and duplex settings */
priv->link = 1;
priv->speed = SPEED_100;
priv->duplex = DUPLEX_FULL;
emac_update_phystatus(priv);
}
if (!netif_running(ndev)) /* debug only - to avoid compiler warning */
emac_dump_regs(priv);
if (netif_msg_drv(priv))
dev_notice(EMAC_DEV, "DaVinci EMAC: Opened %s\n", ndev->name);
if (priv->phy_mask)
phy_start(priv->phydev);
return 0;
rollback:
dev_err(EMAC_DEV, "DaVinci EMAC: request_irq() failed");
for (q = k; k >= 0; k--) {
for (m = i; m >= res->start; m--)
free_irq(m, ndev);
res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k-1);
m = res->end;
}
return -EBUSY;
}
/**
* emac_dev_stop: EMAC device stop
* @ndev: The DaVinci EMAC network adapter
*
* Called when system wants to stop or down the interface. We stop the network
* queue, disable interrupts and cleanup TX/RX channels.
*
* We return the statistics in net_device_stats structure pulled from emac
*/
static int emac_dev_stop(struct net_device *ndev)
{
struct resource *res;
int i = 0;
int irq_num;
struct emac_priv *priv = netdev_priv(ndev);
/* inform the upper layers. */
netif_stop_queue(ndev);
napi_disable(&priv->napi);
netif_carrier_off(ndev);
emac_int_disable(priv);
emac_stop_txch(priv, EMAC_DEF_TX_CH);
emac_stop_rxch(priv, EMAC_DEF_RX_CH);
emac_cleanup_txch(priv, EMAC_DEF_TX_CH);
emac_cleanup_rxch(priv, EMAC_DEF_RX_CH);
emac_write(EMAC_SOFTRESET, 1);
if (priv->phydev)
phy_disconnect(priv->phydev);
/* Free IRQ */
while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i))) {
for (irq_num = res->start; irq_num <= res->end; irq_num++)
free_irq(irq_num, priv->ndev);
i++;
}
if (netif_msg_drv(priv))
dev_notice(EMAC_DEV, "DaVinci EMAC: %s stopped\n", ndev->name);
return 0;
}
/**
* emac_dev_getnetstats: EMAC get statistics function
* @ndev: The DaVinci EMAC network adapter
*
* Called when system wants to get statistics from the device.
*
* We return the statistics in net_device_stats structure pulled from emac
*/
static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev)
{
struct emac_priv *priv = netdev_priv(ndev);
/* update emac hardware stats and reset the registers*/
priv->net_dev_stats.multicast += emac_read(EMAC_RXMCASTFRAMES);
emac_write(EMAC_RXMCASTFRAMES, EMAC_ALL_MULTI_REG_VALUE);
priv->net_dev_stats.collisions += (emac_read(EMAC_TXCOLLISION) +
emac_read(EMAC_TXSINGLECOLL) +
emac_read(EMAC_TXMULTICOLL));
emac_write(EMAC_TXCOLLISION, EMAC_ALL_MULTI_REG_VALUE);
emac_write(EMAC_TXSINGLECOLL, EMAC_ALL_MULTI_REG_VALUE);
emac_write(EMAC_TXMULTICOLL, EMAC_ALL_MULTI_REG_VALUE);
priv->net_dev_stats.rx_length_errors += (emac_read(EMAC_RXOVERSIZED) +
emac_read(EMAC_RXJABBER) +
emac_read(EMAC_RXUNDERSIZED));
emac_write(EMAC_RXOVERSIZED, EMAC_ALL_MULTI_REG_VALUE);
emac_write(EMAC_RXJABBER, EMAC_ALL_MULTI_REG_VALUE);
emac_write(EMAC_RXUNDERSIZED, EMAC_ALL_MULTI_REG_VALUE);
priv->net_dev_stats.rx_over_errors += (emac_read(EMAC_RXSOFOVERRUNS) +
emac_read(EMAC_RXMOFOVERRUNS));
emac_write(EMAC_RXSOFOVERRUNS, EMAC_ALL_MULTI_REG_VALUE);
emac_write(EMAC_RXMOFOVERRUNS, EMAC_ALL_MULTI_REG_VALUE);
priv->net_dev_stats.rx_fifo_errors += emac_read(EMAC_RXDMAOVERRUNS);
emac_write(EMAC_RXDMAOVERRUNS, EMAC_ALL_MULTI_REG_VALUE);
priv->net_dev_stats.tx_carrier_errors +=
emac_read(EMAC_TXCARRIERSENSE);
emac_write(EMAC_TXCARRIERSENSE, EMAC_ALL_MULTI_REG_VALUE);
priv->net_dev_stats.tx_fifo_errors = emac_read(EMAC_TXUNDERRUN);
emac_write(EMAC_TXUNDERRUN, EMAC_ALL_MULTI_REG_VALUE);
return &priv->net_dev_stats;
}
static const struct net_device_ops emac_netdev_ops = {
.ndo_open = emac_dev_open,
.ndo_stop = emac_dev_stop,
.ndo_start_xmit = emac_dev_xmit,
.ndo_set_multicast_list = emac_dev_mcast_set,
.ndo_set_mac_address = emac_dev_setmac_addr,
.ndo_do_ioctl = emac_devioctl,
.ndo_tx_timeout = emac_dev_tx_timeout,
.ndo_get_stats = emac_dev_getnetstats,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = emac_poll_controller,
#endif
};
/**
* davinci_emac_probe: EMAC device probe
* @pdev: The DaVinci EMAC device that we are removing
*
* Called when probing for emac devicesr. We get details of instances and
* resource information from platform init and register a network device
* and allocate resources necessary for driver to perform
*/
static int __devinit davinci_emac_probe(struct platform_device *pdev)
{
int rc = 0;
struct resource *res;
struct net_device *ndev;
struct emac_priv *priv;
unsigned long size;
struct emac_platform_data *pdata;
/* obtain emac clock from kernel */
emac_clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(emac_clk)) {
printk(KERN_ERR "DaVinci EMAC: Failed to get EMAC clock\n");
return -EBUSY;
}
emac_bus_frequency = clk_get_rate(emac_clk);
/* TODO: Probe PHY here if possible */
ndev = alloc_etherdev(sizeof(struct emac_priv));
if (!ndev) {
printk(KERN_ERR "DaVinci EMAC: Error allocating net_device\n");
clk_put(emac_clk);
return -ENOMEM;
}
platform_set_drvdata(pdev, ndev);
priv = netdev_priv(ndev);
priv->pdev = pdev;
priv->ndev = ndev;
priv->msg_enable = netif_msg_init(debug_level, DAVINCI_EMAC_DEBUG);
spin_lock_init(&priv->tx_lock);
spin_lock_init(&priv->rx_lock);
spin_lock_init(&priv->lock);
pdata = pdev->dev.platform_data;
if (!pdata) {
printk(KERN_ERR "DaVinci EMAC: No platfrom data\n");
return -ENODEV;
}
/* MAC addr and PHY mask , RMII enable info from platform_data */
memcpy(priv->mac_addr, pdata->mac_addr, 6);
priv->phy_mask = pdata->phy_mask;
priv->rmii_en = pdata->rmii_en;
priv->version = pdata->version;
/* Get EMAC platform data */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(EMAC_DEV, "DaVinci EMAC: Error getting res\n");
rc = -ENOENT;
goto probe_quit;
}
priv->emac_base_phys = res->start + pdata->ctrl_reg_offset;
size = res->end - res->start + 1;
if (!request_mem_region(res->start, size, ndev->name)) {
dev_err(EMAC_DEV, "DaVinci EMAC: failed request_mem_region() \
for regs\n");
rc = -ENXIO;
goto probe_quit;
}
priv->remap_addr = ioremap(res->start, size);
if (!priv->remap_addr) {
dev_err(EMAC_DEV, "Unable to map IO\n");
rc = -ENOMEM;
release_mem_region(res->start, size);
goto probe_quit;
}
priv->emac_base = priv->remap_addr + pdata->ctrl_reg_offset;
ndev->base_addr = (unsigned long)priv->remap_addr;
priv->ctrl_base = priv->remap_addr + pdata->ctrl_mod_reg_offset;
priv->ctrl_ram_size = pdata->ctrl_ram_size;
priv->emac_ctrl_ram = priv->remap_addr + pdata->ctrl_ram_offset;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(EMAC_DEV, "DaVinci EMAC: Error getting irq res\n");
rc = -ENOENT;
goto no_irq_res;
}
ndev->irq = res->start;
if (!is_valid_ether_addr(priv->mac_addr)) {
DECLARE_MAC_BUF(buf);
/* Use random MAC if none passed */
random_ether_addr(priv->mac_addr);
printk(KERN_WARNING "%s: using random MAC addr: %s\n",
__func__, print_mac(buf, priv->mac_addr));
}
ndev->netdev_ops = &emac_netdev_ops;
SET_ETHTOOL_OPS(ndev, &ethtool_ops);
netif_napi_add(ndev, &priv->napi, emac_poll, EMAC_POLL_WEIGHT);
/* register the network device */
SET_NETDEV_DEV(ndev, &pdev->dev);
rc = register_netdev(ndev);
if (rc) {
dev_err(EMAC_DEV, "DaVinci EMAC: Error in register_netdev\n");
rc = -ENODEV;
goto netdev_reg_err;
}
clk_enable(emac_clk);
/* MII/Phy intialisation, mdio bus registration */
emac_mii = mdiobus_alloc();
if (emac_mii == NULL) {
dev_err(EMAC_DEV, "DaVinci EMAC: Error allocating mii_bus\n");
rc = -ENOMEM;
goto mdio_alloc_err;
}
priv->mii_bus = emac_mii;
emac_mii->name = "emac-mii",
emac_mii->read = emac_mii_read,
emac_mii->write = emac_mii_write,
emac_mii->reset = emac_mii_reset,
emac_mii->irq = mii_irqs,
emac_mii->phy_mask = ~(priv->phy_mask);
emac_mii->parent = &pdev->dev;
emac_mii->priv = priv->remap_addr + pdata->mdio_reg_offset;
snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%x", priv->pdev->id);
mdio_max_freq = pdata->mdio_max_freq;
emac_mii->reset(emac_mii);
/* Register the MII bus */
rc = mdiobus_register(emac_mii);
if (rc)
goto mdiobus_quit;
if (netif_msg_probe(priv)) {
dev_notice(EMAC_DEV, "DaVinci EMAC Probe found device "\
"(regs: %p, irq: %d)\n",
(void *)priv->emac_base_phys, ndev->irq);
}
return 0;
mdiobus_quit:
mdiobus_free(emac_mii);
netdev_reg_err:
mdio_alloc_err:
no_irq_res:
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, res->end - res->start + 1);
iounmap(priv->remap_addr);
probe_quit:
clk_put(emac_clk);
free_netdev(ndev);
return rc;
}
/**
* davinci_emac_remove: EMAC device remove
* @pdev: The DaVinci EMAC device that we are removing
*
* Called when removing the device driver. We disable clock usage and release
* the resources taken up by the driver and unregister network device
*/
static int __devexit davinci_emac_remove(struct platform_device *pdev)
{
struct resource *res;
struct net_device *ndev = platform_get_drvdata(pdev);
struct emac_priv *priv = netdev_priv(ndev);
dev_notice(&ndev->dev, "DaVinci EMAC: davinci_emac_remove()\n");
clk_disable(emac_clk);
platform_set_drvdata(pdev, NULL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mdiobus_unregister(priv->mii_bus);
mdiobus_free(priv->mii_bus);
release_mem_region(res->start, res->end - res->start + 1);
unregister_netdev(ndev);
free_netdev(ndev);
iounmap(priv->remap_addr);
clk_disable(emac_clk);
clk_put(emac_clk);
return 0;
}
/**
* davinci_emac_driver: EMAC platform driver structure
*
* We implement only probe and remove functions - suspend/resume and
* others not supported by this module
*/
static struct platform_driver davinci_emac_driver = {
.driver = {
.name = "davinci_emac",
.owner = THIS_MODULE,
},
.probe = davinci_emac_probe,
.remove = __devexit_p(davinci_emac_remove),
};
/**
* davinci_emac_init: EMAC driver module init
*
* Called when initializing the driver. We register the driver with
* the platform.
*/
static int __init davinci_emac_init(void)
{
return platform_driver_register(&davinci_emac_driver);
}
module_init(davinci_emac_init);
/**
* davinci_emac_exit: EMAC driver module exit
*
* Called when exiting the driver completely. We unregister the driver with
* the platform and exit
*/
static void __exit davinci_emac_exit(void)
{
platform_driver_unregister(&davinci_emac_driver);
}
module_exit(davinci_emac_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("DaVinci EMAC Maintainer: Anant Gole <anantgole@ti.com>");
MODULE_DESCRIPTION("DaVinci EMAC Ethernet driver");
......@@ -128,6 +128,15 @@ comment "I2C RTC drivers"
if I2C
config RTC_DRV_DAVINCI_EVM
tristate "TI DaVinci EVM RTC"
depends on RTC_CLASS && I2C_DAVINCI && MACH_DAVINCI_EVM
help
Supports the RTC firmware in the MSP430 on the DaVinci EVM.
This driver can also be built as a module. If so, the module
will be called rtc-davinci-evm.
config RTC_DRV_DS1307
tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
help
......
......@@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_DAVINCI_EVM) += rtc-davinci-evm.o
obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
......
/*
* rtc-davinci-evm.c
*
* Copyright (C) 2004 Texas Instruments Inc
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <asm/mach-types.h>
#include <mach/i2c-client.h>
/* REVISIT
* - the firmware expects no I2C writes at all, not just no RTC-via-I2C
* writes, for 100 usec after i2c read or write... that can't be
* assured here.
*
* - this am vs pm thing is bizarre ... firmware should just do a 24 hour
* clock, rather than 12 hour with hidden am/pm (we must guess).
* similarly with it trying to handle DST for us...
*
* - better (and simpler!!) firmware would support an RTC alarm, and just
* count seconds since some UTC instant, letting Linux handle calendar
* issues (leapyear, day of week, etc) and DST.
*/
static unsigned char am;
static int evm_read_time(struct device *dev, struct rtc_time *tm)
{
char rtcdata [9];
rtcdata[0] = 2;
rtcdata[1] = 1;
davinci_i2c_write(2, rtcdata, 0x23);
msleep(1);
davinci_i2c_read(9, rtcdata, 0x23);
msleep(1);
/* FIXME the RTC reports 12-hour time, without an AM/PM indicator,
* but Linux requires that we report 24 hour time...
*/
tm->tm_year = BCD_TO_BIN(rtcdata[3]) * 100
+ BCD_TO_BIN(rtcdata[2])
- 1900;
tm->tm_mon = BCD_TO_BIN(rtcdata[4]);
tm->tm_mday = BCD_TO_BIN(rtcdata[5]);
tm->tm_hour = BCD_TO_BIN(rtcdata[6]);
tm->tm_min = BCD_TO_BIN(rtcdata[7]);
tm->tm_sec = BCD_TO_BIN(rtcdata[8]);
return 0;
}
static void am_or_pm(struct device *dev)
{
char rtcdata [9];
struct rtc_time tm, time, temp;
unsigned char mon, day, hrs, min, sec;
unsigned char yr_low, yr_high;
unsigned int yrs;
evm_read_time(dev, &tm);
temp = tm;
yrs = temp.tm_year + 1900;
yr_high = yrs / 100;
yr_low = yrs % 100;
mon = temp.tm_mon + 1;
day = temp.tm_mday;
min = 59;
sec = 59;
hrs = 11;
rtcdata [0] = 9;
rtcdata [1] = 0;
rtcdata [2] = BIN_TO_BCD(yr_low);
rtcdata [3] = BIN_TO_BCD(yr_high);
mon--;
rtcdata [4] = BIN_TO_BCD(mon);
rtcdata [5] = BIN_TO_BCD(day);
rtcdata [6] = BIN_TO_BCD(hrs);
rtcdata [7] = BIN_TO_BCD(min);
rtcdata [8] = BIN_TO_BCD(sec);
davinci_i2c_write(9, rtcdata, 0x23);
msleep(1);
msleep(1000);
evm_read_time(dev, &time);
if (time.tm_mday == temp.tm_mday)
am = 1;
else
am = 0;
davinci_i2c_write(9, rtcdata, 0x23);
msleep(1);
msleep(1000);
yrs = tm.tm_year + 1900;
yr_high = yrs / 100;
yr_low = yrs % 100;
mon = tm.tm_mon + 1;
day = tm.tm_mday;
min = tm.tm_min;
hrs = tm.tm_hour;
if (tm.tm_sec < 58)
sec = tm.tm_sec + 2;
else
sec = 59;
davinci_i2c_write(9, rtcdata, 0x23);
msleep(1);
}
static int evm_set_time(struct device *dev, struct rtc_time *tm)
{
char rtcdata [9];
char ampmdata [9];
unsigned char mon, day, hrs = 0, min, sec, leap_yr;
unsigned char yr_low, yr_high;
unsigned int yrs;
am_or_pm(dev);
yrs = tm->tm_year + 1900;
yr_high = yrs / 100;
yr_low = yrs % 100;
mon = tm->tm_mon;
hrs = tm->tm_hour;
day = tm->tm_mday;
min = tm->tm_min;
sec = tm->tm_sec;
leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
if (am == 1 && tm->tm_hour <= 12) {
hrs = tm->tm_hour;
if (tm->tm_hour == 0)
hrs = tm->tm_hour + 12;
} else if ((am == 1 && tm->tm_hour > 12)
|| (am == 0 && tm->tm_hour < 12)) {
unsigned char mon1 = mon, day1 = day, hrs1 = 11;
unsigned char min1 = 59, sec1 = 59;
unsigned char yr_low1 = yr_low, yr_high1 = yr_high;
ampmdata [0] = 9;
ampmdata [1] = 0;
ampmdata [2] = BIN_TO_BCD(yr_low1);
ampmdata [3] = BIN_TO_BCD(yr_high1);
ampmdata [4] = BIN_TO_BCD(mon1);
ampmdata [5] = BIN_TO_BCD(day1);
ampmdata [6] = BIN_TO_BCD(hrs1);
ampmdata [7] = BIN_TO_BCD(min1);
ampmdata [8] = BIN_TO_BCD(sec1);
davinci_i2c_write(9, ampmdata, 0x23);
msleep(1);
msleep(1000);
am = (am == 1) ? 0 : 1;
if (!am)
hrs = tm->tm_hour - 12;
else if (tm->tm_hour == 0)
hrs = tm->tm_hour + 12;
} else if (am == 0 && tm->tm_hour > 12)
hrs = tm->tm_hour - 12;
rtcdata [0] = 9;
rtcdata [1] = 0;
rtcdata [2] = BIN_TO_BCD(yr_low);
rtcdata [3] = BIN_TO_BCD(yr_high);
rtcdata [4] = BIN_TO_BCD(mon);
rtcdata [5] = BIN_TO_BCD(day);
rtcdata [6] = BIN_TO_BCD(hrs);
rtcdata [7] = BIN_TO_BCD(min);
rtcdata [8] = BIN_TO_BCD(sec);
davinci_i2c_write(9, rtcdata, 0x23);
msleep(1);
return 0;
}
static struct rtc_class_ops evm_rtc_ops = {
.read_time = evm_read_time,
.set_time = evm_set_time,
};
static int __devinit evm_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
/* the 2005-12-05 firmware doesn't issue RTC alarms on GPIO(7);
* it only uses IRQ for card detect irqs with removable media.
* plus it also hides the am/pm indicator and does magic DST...
*/
rtc = rtc_device_register(pdev->name, &pdev->dev,
&evm_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
dev_warn(&pdev->dev, "WARNING: hours 12-23 are misreported as "
"duplicate hours 00-11\n");
platform_set_drvdata(pdev, rtc);
return 0;
}
static int __devexit evm_rtc_remove(struct platform_device *pdev)
{
rtc_device_unregister(platform_get_drvdata(pdev));
return 0;
}
static struct platform_driver evm_rtc_driver = {
.driver = {
.name = "rtc_davinci_evm",
},
.probe = evm_rtc_probe,
.remove = __devexit_p(evm_rtc_remove),
};
static int evm_rtc_init(void)
{
if (!machine_is_davinci_evm())
return -ENODEV;
return platform_driver_register(&evm_rtc_driver);
}
module_init(evm_rtc_init);
static void evm_rtc_exit(void)
{
platform_driver_unregister(&evm_rtc_driver);
}
module_exit(evm_rtc_exit);
MODULE_DESCRIPTION("RTC driver for TI DaVinci EVM");
MODULE_LICENSE("GPL");
......@@ -35,13 +35,14 @@
#include <mach/hardware.h>
#include <mach/memory.h>
#include <mach/gpio.h>
#include <mach/cputype.h>
#include <asm/mach-types.h>
#include "musb_core.h"
#ifdef CONFIG_MACH_DAVINCI_EVM
#define GPIO_nVBUS_DRV 87
#define GPIO_nVBUS_DRV 120
#endif
#include "davinci.h"
......@@ -417,6 +418,21 @@ int __init musb_platform_init(struct musb *musb)
__raw_writel(phy_ctrl, USB_PHY_CTRL);
}
/* On dm355, the default-A state machine needs DRVVBUS control.
* If we won't be a host, there's no need to turn it on.
*/
if (cpu_is_davinci_dm355()) {
u32 deepsleep = __raw_readl(DM355_DEEPSLEEP);
if (is_host_enabled(musb)) {
deepsleep &= ~DRVVBUS_OVERRIDE;
} else {
deepsleep &= ~DRVVBUS_FORCE;
deepsleep |= DRVVBUS_OVERRIDE;
}
__raw_writel(deepsleep, DM355_DEEPSLEEP);
}
/* reset the controller */
musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1);
......@@ -439,6 +455,15 @@ int musb_platform_exit(struct musb *musb)
if (is_host_enabled(musb))
del_timer_sync(&otg_workaround);
/* force VBUS off */
if (cpu_is_davinci_dm355()) {
u32 deepsleep = __raw_readl(DM355_DEEPSLEEP);
deepsleep &= ~DRVVBUS_FORCE;
deepsleep |= DRVVBUS_OVERRIDE;
__raw_writel(deepsleep, DM355_DEEPSLEEP);
}
davinci_source_power(musb, 0 /*off*/, 1);
/* delay, to avoid problems with module reload */
......
......@@ -1973,6 +1973,21 @@ config FB_IBM_GXT4500
Say Y here to enable support for the IBM GXT4500P display
adaptor, found on some IBM System P (pSeries) machines.
config FB_DAVINCI
bool "Davinci Framebuffer support"
depends on FB && ARCH_DAVINCI
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
help
This is the frame buffer device driver for the DaVinci video
hardware found on the TI DaVinci EVM. If
unsure, say N.
config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
depends on FB
config FB_PS3
tristate "PS3 GPU framebuffer driver"
depends on FB && PS3_PS3AV
......
......@@ -135,6 +135,7 @@ obj-$(CONFIG_FB_OF) += offb.o
obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o
obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DAVINCI) += davincifb.o
# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
......
/*
* drivers/video/davincifb.c
*
* Framebuffer driver for Texas Instruments DaVinci display controller.
*
* Copyright (C) 2006 Texas Instruments, Inc.
* Rishi Bhattacharya <support@ti.com>
*
* Leveraged from the framebuffer driver for OMAP24xx
* written by Andy Lowe (source@mvista.com)
* Copyright (C) 2004 MontaVista Software, Inc.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <video/davincifb.h>
#include <asm/system.h>
#define MODULE_NAME "davincifb"
/* Output Format Selection */
#define MULTIPLE_BUFFERING 1
#ifdef MULTIPLE_BUFFERING
#define DOUBLE_BUF 2
#define TRIPLE_BUF 3
#else
#define DOUBLE_BUF 1
#define TRIPLE_BUF 1
#endif
/*
* display controller register I/O routines
*/
static __inline__ u32 dispc_reg_in(u32 reg)
{
return ioread32(IO_ADDRESS(reg));
}
static __inline__ u32 dispc_reg_out(u32 reg, u32 val)
{
iowrite32(val, IO_ADDRESS(reg));
return (val);
}
static __inline__ u32 dispc_reg_merge(u32 reg, u32 val, u32 mask)
{
u32 new_val = (ioread32(IO_ADDRESS(reg)) & ~mask) | (val & mask);
iowrite32(new_val, IO_ADDRESS(reg));
return (new_val);
}
/* There are 4 framebuffers, each represented by an fb_info and
* a dm_win_info structure */
#define OSD0_FBNAME "dm_osd0_fb"
#define OSD1_FBNAME "dm_osd1_fb"
#define VID0_FBNAME "dm_vid0_fb"
#define VID1_FBNAME "dm_vid1_fb"
/* usage: if (is_win(info->fix.id, OSD0)) ... */
#define is_win(name, x) ((strcmp(name, x ## _FBNAME) == 0) ? 1 : 0)
struct dm_win_info {
struct fb_info info;
/* X and Y position */
unsigned int x, y;
/* framebuffer area */
dma_addr_t fb_base_phys;
unsigned long fb_base;
unsigned long fb_size;
u32 pseudo_palette[17];
/* flag to identify if framebuffer area is fixed already or not */
int alloc_fb_mem;
unsigned long sdram_address;
struct dm_info *dm;
};
static struct dm_info {
struct dm_win_info *osd0;
struct dm_win_info *osd1;
struct dm_win_info *vid0;
struct dm_win_info *vid1;
/* to map the registers */
dma_addr_t mmio_base_phys;
unsigned long mmio_base;
unsigned long mmio_size;
wait_queue_head_t vsync_wait;
unsigned long vsync_cnt;
int timeout;
/* this is the function that configures the output device (NTSC/PAL/LCD)
* for the required output format (composite/s-video/component/rgb)
*/
void (*output_device_config) (int on);
struct device *dev;
} dm_static;
static struct dm_info *dm = &dm_static;
static struct fb_ops davincifb_ops;
#define BASEX 0x80
#define BASEY 0x12
#define DISP_XRES 720
#define DISP_YRES 480
#define DISP_MEMY 576
/* Random value chosen for now. Should be within the panel's supported range */
#define LCD_PANEL_CLOCK 180000
/* All window widths have to be rounded up to a multiple of 32 bytes */
/* The OSD0 window has to be always within VID0. Plus, since it is in RGB565
* mode, it _cannot_ overlap with VID1.
* For defaults, we are setting the OSD0 window to be displayed in the top
* left quadrant of the screen, and the VID1 in the bottom right quadrant.
* So the default 'xres' and 'yres' are set to half of the screen width and
* height respectively. Note however that the framebuffer size is allocated
* for the full screen size so the user can change the 'xres' and 'yres' by
* using the FBIOPUT_VSCREENINFO ioctl within the limits of the screen size.
*/
#define round_32(width) ((((width) + 31) / 32) * 32 )
#define OSD0_XRES round_32((DISP_XRES)*16/8) * 8/16 /* pixels */
#define OSD0_YRES DISP_YRES
#define OSD0_FB_PHY 0
#define OSD0_FB_SIZE (round_32((DISP_XRES)*16/8) * DISP_MEMY * DOUBLE_BUF)
/* 16 bpp, Double buffered */
static struct fb_var_screeninfo osd0_default_var = {
.xres = OSD0_XRES,
.yres = OSD0_YRES,
.xres_virtual = OSD0_XRES,
.yres_virtual = OSD0_YRES * DOUBLE_BUF,
.xoffset = 0,
.yoffset = 0,
.bits_per_pixel = 16,
.grayscale = 0,
.red = {11, 5, 0},
.green = {5, 6, 0},
.blue = {0, 5, 0},
.transp = {0, 0, 0},
.nonstd = 0,
.activate = FB_ACTIVATE_NOW,
.height = -1,
.width = -1,
.accel_flags = 0,
.pixclock = LCD_PANEL_CLOCK, /* picoseconds */
.left_margin = 40, /* pixclocks */
.right_margin = 4, /* pixclocks */
.upper_margin = 8, /* line clocks */
.lower_margin = 2, /* line clocks */
.hsync_len = 4, /* pixclocks */
.vsync_len = 2, /* line clocks */
.sync = 0,
.vmode = FB_VMODE_INTERLACED,
};
/* Using the full screen for OSD1 by default */
#define OSD1_XRES round_32(DISP_XRES*4/8) * 8/4 /* pixels */
#define OSD1_YRES DISP_YRES
#define OSD1_FB_PHY 0
#define OSD1_FB_SIZE (round_32(DISP_XRES*4/8) * DISP_MEMY * DOUBLE_BUF)
static struct fb_var_screeninfo osd1_default_var = {
.xres = DISP_XRES,
.yres = OSD1_YRES,
.xres_virtual = OSD1_XRES,
.yres_virtual = OSD1_YRES * DOUBLE_BUF,
.xoffset = 0,
.yoffset = 0,
.bits_per_pixel = 4,
.activate = FB_ACTIVATE_NOW,
.accel_flags = 0,
.pixclock = LCD_PANEL_CLOCK, /* picoseconds */
.vmode = FB_VMODE_INTERLACED,
};
/* Using the full screen for OSD0 by default */
#define VID0_XRES round_32(DISP_XRES*16/8) * 8/16 /* pixels */
#define VID0_YRES DISP_YRES
#define VID0_FB_PHY 0
#define VID0_FB_SIZE (round_32(DISP_XRES*16/8) * DISP_MEMY * TRIPLE_BUF)
static struct fb_var_screeninfo vid0_default_var = {
.xres = VID0_XRES,
.yres = VID0_YRES,
.xres_virtual = VID0_XRES,
.yres_virtual = VID0_YRES * TRIPLE_BUF,
.xoffset = 0,
.yoffset = 0,
.bits_per_pixel = 16,
.activate = FB_ACTIVATE_NOW,
.accel_flags = 0,
.pixclock = LCD_PANEL_CLOCK, /* picoseconds */
.vmode = FB_VMODE_INTERLACED,
};
/* Using the bottom right quadrant of the screen screen for VID1 by default,
* but keeping the framebuffer allocated for the full screen, so the user can
* change the 'xres' and 'yres' later using the FBIOPUT_VSCREENINFO ioctl.
*/
#define VID1_BPP 16 /* Video1 can be in YUV or RGB888 format */
#define VID1_XRES round_32(DISP_XRES*16/8) * 8/16 /* pixels */
#define VID1_YRES DISP_YRES
#define VID1_FB_PHY 0
#define VID1_FB_SIZE (round_32(DISP_XRES*16/8) * DISP_MEMY * TRIPLE_BUF)
static struct fb_var_screeninfo vid1_default_var = {
.xres = VID1_XRES,
.yres = VID1_YRES,
.xres_virtual = VID1_XRES,
.yres_virtual = VID1_YRES * TRIPLE_BUF,
.xoffset = 0,
.yoffset = 0,
.bits_per_pixel = VID1_BPP,
.activate = FB_ACTIVATE_NOW,
.accel_flags = 0,
.pixclock = LCD_PANEL_CLOCK, /* picoseconds */
.vmode = FB_VMODE_INTERLACED,
};
#define x_pos(w) ((w)->x)
#define y_pos(w) ((w)->y)
static struct dmparams_t {
u8 output;
u8 format;
u8 windows; /* bitmap flag based on VID0, VID1, OSD0, OSD1
* definitions in header file */
u32 vid0_xres;
u32 vid0_yres;
u32 vid0_xpos;
u32 vid0_ypos;
u32 vid1_xres;
u32 vid1_yres;
u32 vid1_xpos;
u32 vid1_ypos;
u32 osd0_xres;
u32 osd0_yres;
u32 osd0_xpos;
u32 osd0_ypos;
u32 osd1_xres;
u32 osd1_yres;
u32 osd1_xpos;
u32 osd1_ypos;
} dmparams = {
NTSC, /* output */
COMPOSITE, /* format */
(1 << VID0) | (1 << VID1) | (1 << OSD0) | (1 << OSD1),
/* windows registered */
720, 480, 0, 0, /* vid0 size and position */
720, 480, 0, 0, /* vid1 size and position */
720, 480, 0, 0, /* osd0 size and position */
720, 480, 0, 0, /* osd1 size and position */
};
/* Must do checks against the limits of the output device */
static int davincifb_venc_check_mode(const struct dm_win_info *w,
const struct fb_var_screeninfo *var)
{
return 0;
}
static void set_sdram_params(char *id, u32 addr, u32 line_length);
static irqreturn_t davincifb_isr(int irq, void *arg)
{
struct dm_info *dm = (struct dm_info *)arg;
unsigned long addr=0;
if ((dispc_reg_in(VENC_VSTAT) & 0x00000010) == 0x10) {
xchg(&addr, dm->osd0->sdram_address);
if (addr) {
set_sdram_params(dm->osd0->info.fix.id,
dm->osd0->sdram_address,
dm->osd0->info.fix.line_length);
dm->osd0->sdram_address = 0;
}
addr = 0;
xchg(&addr, dm->osd1->sdram_address);
if (addr) {
set_sdram_params(dm->osd1->info.fix.id,
dm->osd1->sdram_address,
dm->osd1->info.fix.line_length);
dm->osd1->sdram_address = 0;
}
addr = 0;
xchg(&addr, dm->vid0->sdram_address);
if (addr) {
set_sdram_params(dm->vid0->info.fix.id,
dm->vid0->sdram_address,
dm->vid0->info.fix.line_length);
dm->vid0->sdram_address = 0;
}
addr = 0;
xchg(&addr, dm->vid1->sdram_address);
if (addr) {
set_sdram_params(dm->vid1->info.fix.id,
dm->vid1->sdram_address,
dm->vid1->info.fix.line_length);
dm->vid1->sdram_address = 0;
}
return IRQ_HANDLED;
} else {
++dm->vsync_cnt;
wake_up_interruptible(&dm->vsync_wait);
return IRQ_HANDLED;
}
return IRQ_HANDLED;
}
/* Wait for a vsync interrupt. This routine sleeps so it can only be called
* from process context.
*/
static int davincifb_wait_for_vsync(struct dm_win_info *w)
{
struct dm_info *dm = w->dm;
wait_queue_t wq;
unsigned long cnt;
int ret;
init_waitqueue_entry(&wq, current);
cnt = dm->vsync_cnt;
ret = wait_event_interruptible_timeout(dm->vsync_wait,
cnt != dm->vsync_cnt,
dm->timeout);
if (ret < 0)
return (ret);
if (ret == 0)
return (-ETIMEDOUT);
return (0);
}
/* Sets a uniform attribute value over a rectangular area on the attribute
* window. The attribute value (0 to 7) is passed through the fb_fillrect's
* color parameter.
*/
static int davincifb_set_attr_blend(struct fb_fillrect *r)
{
struct fb_info *info = &dm->osd1->info;
struct fb_var_screeninfo *var = &dm->osd1->info.var;
unsigned long start = 0;
u8 blend;
u32 width_bytes;
if (r->dx + r->width > var->xres_virtual)
return -EINVAL;
if (r->dy + r->height > var->yres_virtual)
return -EINVAL;
if (r->color < 0 || r->color > 7)
return -EINVAL;
/* since bits_per_pixel = 4, this will truncate the width if it is
* not even. Similarly r->dx will be rounded down to an even pixel.
* ... Do we want to return an error otherwise?
*/
width_bytes = r->width * var->bits_per_pixel / 8;
start = dm->osd1->fb_base + r->dy * info->fix.line_length
+ r->dx * var->bits_per_pixel / 8;
blend = (((u8) r->color & 0xf) << 4) | ((u8) r->color);
while (r->height--) {
start += info->fix.line_length;
memset((void *)start, blend, width_bytes);
}
return 0;
}
/* These position parameters are given through fb_var_screeninfo.
* xp = var.reserved[0], yp = var.reserved[1],
* xl = var.xres, yl = var.yres
*/
static void set_win_position(char *id, u32 xp, u32 yp, u32 xl, u32 yl)
{
int i = 0;
if (is_win(id, VID0)) {
i = 0;
} else if (is_win(id, VID1)) {
i = 1;
} else if (is_win(id, OSD0)) {
i = 2;
} else if (is_win(id, OSD1)) {
i = 3;
}
dispc_reg_out(OSD_WINXP(i), xp);
dispc_reg_out(OSD_WINYP(i), yp);
dispc_reg_out(OSD_WINXL(i), xl);
dispc_reg_out(OSD_WINYL(i), yl);
}
static inline void get_win_position(struct dm_win_info *w,
u32 * xp, u32 * yp, u32 * xl, u32 * yl)
{
struct fb_var_screeninfo *v = &w->info.var;
*xp = x_pos(w);
*yp = y_pos(w);
*xl = v->xres;
*yl = v->yres;
}
/* Returns 1 if the windows overlap, 0 otherwise */
static int window_overlap(struct dm_win_info *w, u32 xp, u32 yp, u32 xl, u32 yl)
{
u32 _xp = 0, _yp = 0, _xl = 0, _yl = 0;
#define OVERLAP(x1, y1, x2, y2, x3, y3, x4, y4) \
(!( ((x1)<(x3) && (x2)<(x3)) || ((x1)>(x4) && (x2)>(x4)) || \
((y1)<(y3) && (y2)<(y3)) || ((y1)>(y4) && (y2)>(y4)) ) \
)
if (!w)
return (0);
get_win_position(w, &_xp, &_yp, &_xl, &_yl);
return (OVERLAP(xp, yp, xp + xl, yp + yl,
_xp, _yp, _xp + _xl, _yp + _yl));
#undef OVERLAP
}
/* Returns 1 if the window parameters are within VID0, 0 otherwise */
static int within_vid0_limits(u32 xp, u32 yp, u32 xl, u32 yl)
{
u32 vid0_xp = 0, vid0_yp = 0, vid0_xl = 0, vid0_yl = 0;
if (!dm->vid0)
return (1);
get_win_position(dm->vid0, &vid0_xp, &vid0_yp, &vid0_xl, &vid0_yl);
if ((xp >= vid0_xp) && (yp >= vid0_yp) &&
(xp + xl <= vid0_xp + vid0_xl) && (yp + yl <= vid0_yp + vid0_yl))
return (1);
return (0);
}
/* VID0 must be large enough to hold all other windows */
static int check_new_vid0_size(u32 xp0, u32 yp0, u32 xl0, u32 yl0)
{
u32 _xp = 0, _yp = 0, _xl = 0, _yl = 0;
#define WITHIN_LIMITS \
((_xp >= xp0) && (_yp >= yp0) && \
(_xp + _xl <= xp0 + xl0) && (_yp + _yl <= yp0 + yl0))
if (dm->osd0) {
get_win_position(dm->osd0, &_xp, &_yp, &_xl, &_yl);
if (!WITHIN_LIMITS)
return (-EINVAL);
}
if (dm->osd1) {
get_win_position(dm->osd1, &_xp, &_yp, &_xl, &_yl);
if (!WITHIN_LIMITS)
return (-EINVAL);
}
if (dm->vid1) {
get_win_position(dm->vid1, &_xp, &_yp, &_xl, &_yl);
if (!WITHIN_LIMITS)
return (-EINVAL);
}
return (0);
#undef WITHIN_LIMITS
}
/**
* davincifb_check_var - Validates a var passed in.
* @var: frame buffer variable screen structure
* @info: frame buffer structure that represents a single frame buffer
*
* Checks to see if the hardware supports the state requested by
* var passed in. This function does not alter the hardware state!!!
* This means the data stored in struct fb_info and struct xxx_par do
* not change. This includes the var inside of struct fb_info.
* Do NOT change these. This function can be called on its own if we
* intent to only test a mode and not actually set it.
* If the var passed in is slightly off by what the hardware can support
* then we alter the var PASSED in to what we can do.
*
* Returns negative errno on error, or zero on success.
*/
static int davincifb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
const struct dm_win_info *w = (const struct dm_win_info *)info->par;
struct fb_var_screeninfo v;
/* Rules:
* 1) Vid1, OSD0, OSD1 and Cursor must be fully contained inside of Vid0.
* 2) Vid0 and Vid1 are both set to accept YUV 4:2:2 (for now).
* 3) OSD window data is always packed into 32-bit words and left justified.
* 4) Each horizontal line of window data must be a multiple of 32 bytes.
* 32 bytes = 32 bytes / 2 bytes per pixel = 16 pixels.
* This implies that 'xres' must be a multiple of 32 bytes.
* 5) The offset registers hold the distance between the start of one line and
* the start of the next. This offset value must be a multiple of 32 bytes.
* This implies that 'xres_virtual' is also a multiple of 32 bytes. Note
* that 'xoffset' needn't be a multiple of 32 bytes.
* 6) OSD0 is set to accept RGB565.
* dispc_reg_merge(OSD_OSDWIN0ND, OSD_OSDWIN0ND_RGB0E, OSD_OSDWIN0ND_RGB0E)
* 7) OSD1 is set to be the attribute window.
* 8) Vid1 startX = Vid0 startX + N * 16 pixels (32 bytes)
* 9) Vid1 width = (16*N - 8) pixels
* 10) When one of the OSD windows is in RGB565, it cannot overlap with Vid1.
* 11) Vid1 start X position must be offset a multiple of 16 pixels from the
* left edge of Vid0.
*/
memcpy(&v, var, sizeof(v));
return (0);
/* do board-specific checks on the var */
if (davincifb_venc_check_mode(w, &v))
return (-EINVAL);
if (v.xres_virtual < v.xres || v.yres_virtual < v.yres)
return (-EINVAL);
if (v.xoffset > v.xres_virtual - v.xres)
return (-EINVAL);
if (v.yoffset > v.yres_virtual - v.yres)
return (-EINVAL);
if ((v.xres * v.bits_per_pixel / 8) % 32 || (v.xres_virtual * v.bits_per_pixel / 8) % 32) /* Rules 4, 5 */
return (-EINVAL);
if (v.xres_virtual * v.yres_virtual * v.bits_per_pixel / 8 > w->fb_size)
return (-EINVAL);
if (!is_win(info->fix.id, VID0)) {
/* Rule 1 */
if (!within_vid0_limits(x_pos(w), y_pos(w), v.xres, v.yres))
return (-EINVAL);
}
if (is_win(info->fix.id, OSD0)) {
/* Rule 10 */
if (window_overlap(w->dm->vid1,
x_pos(w), y_pos(w), v.xres, v.yres))
return (-EINVAL);
/* Rule 5 */
v.bits_per_pixel = 16;
v.red.offset = 11;
v.green.offset = 5;
v.blue.offset = 0;
v.red.length = 5;
v.green.length = 6;
v.blue.length = 5;
v.transp.offset = v.transp.length = 0;
v.red.msb_right = v.green.msb_right = v.blue.msb_right
= v.transp.msb_right = 0;
v.nonstd = 0;
v.accel_flags = 0;
} else if (is_win(info->fix.id, OSD1)) {
v.bits_per_pixel = 4;
} else if (is_win(info->fix.id, VID0)) {
if (check_new_vid0_size(x_pos(w), y_pos(w), v.xres, v.yres))
return (-EINVAL);
v.bits_per_pixel = 16;
} else if (is_win(info->fix.id, VID1)) {
/* Rule 11 */
if ((x_pos(w->dm->vid0) - x_pos(w)) % 16)
return (-EINVAL);
/* Video1 may be in YUV or RGB888 format */
if ((v.bits_per_pixel != 16) && (v.bits_per_pixel != 32))
return (-EINVAL);
} else
return (-EINVAL);
memcpy(var, &v, sizeof(v));
return (0);
}
/* Interlaced = Frame mode, Non-interlaced = Field mode */
static void set_interlaced(char *id, unsigned int on)
{
on = (on == 0) ? 0 : ~0;
if (is_win(id, VID0))
dispc_reg_merge(OSD_VIDWINMD, on, OSD_VIDWINMD_VFF0);
else if (is_win(id, VID1))
dispc_reg_merge(OSD_VIDWINMD, on, OSD_VIDWINMD_VFF1);
else if (is_win(id, OSD0))
dispc_reg_merge(OSD_OSDWIN0MD, on, OSD_OSDWIN0MD_OFF0);
else if (is_win(id, OSD1))
dispc_reg_merge(OSD_OSDWIN1MD, on, OSD_OSDWIN1MD_OFF1);
}
/* For zooming, we just have to set the start of framebuffer, the zoom factors
* and the display size. The hardware will then read only
* (display size / zoom factor) area of the framebuffer and zoom and
* display it. In the following function, we assume that the start of
* framebuffer and the display size parameters are set already.
*/
static void set_zoom(int WinID, int h_factor, int v_factor)
{
switch (WinID) {
case 0: //VID0
dispc_reg_merge(OSD_VIDWINMD,
h_factor << OSD_VIDWINMD_VHZ0_SHIFT,
OSD_VIDWINMD_VHZ0);
dispc_reg_merge(OSD_VIDWINMD,
v_factor << OSD_VIDWINMD_VVZ0_SHIFT,
OSD_VIDWINMD_VVZ0);
break;
case 1: //VID1
dispc_reg_merge(OSD_VIDWINMD,
h_factor << OSD_VIDWINMD_VHZ1_SHIFT,
OSD_VIDWINMD_VHZ1);
dispc_reg_merge(OSD_VIDWINMD,
v_factor << OSD_VIDWINMD_VVZ1_SHIFT,
OSD_VIDWINMD_VVZ1);
break;
case 2: //OSD0
dispc_reg_merge(OSD_OSDWIN0MD,
h_factor << OSD_OSDWIN0MD_OHZ0_SHIFT,
OSD_OSDWIN0MD_OHZ0);
dispc_reg_merge(OSD_OSDWIN0MD,
v_factor << OSD_OSDWIN0MD_OVZ0_SHIFT,
OSD_OSDWIN0MD_OVZ0);
break;
case 3:
dispc_reg_merge(OSD_OSDWIN1MD,
h_factor << OSD_OSDWIN1MD_OHZ1_SHIFT,
OSD_OSDWIN1MD_OHZ1);
dispc_reg_merge(OSD_OSDWIN1MD,
v_factor << OSD_OSDWIN1MD_OVZ1_SHIFT,
OSD_OSDWIN1MD_OVZ1);
break;
}
}
/* Chooses the ROM CLUT for now. Can be extended later. */
static void set_bg_color(u8 clut, u8 color_offset)
{
clut = 0; /* 0 = ROM, 1 = RAM */
dispc_reg_merge(OSD_MODE, OSD_MODE_BCLUT & clut, OSD_MODE_BCLUT);
dispc_reg_merge(OSD_MODE, color_offset << OSD_MODE_CABG_SHIFT,
OSD_MODE_CABG);
}
static void set_sdram_params(char *id, u32 addr, u32 line_length)
{
/* The parameters to be written to the registers should be in
* multiple of 32 bytes
*/
addr = addr; /* div by 32 */
line_length = line_length / 32;
if (is_win(id, VID0)) {
dispc_reg_out(OSD_VIDWIN0ADR, addr);
dispc_reg_out(OSD_VIDWIN0OFST, line_length);
} else if (is_win(id, VID1)) {
dispc_reg_out(OSD_VIDWIN1ADR, addr);
dispc_reg_out(OSD_VIDWIN1OFST, line_length);
} else if (is_win(id, OSD0)) {
dispc_reg_out(OSD_OSDWIN0ADR, addr);
dispc_reg_out(OSD_OSDWIN0OFST, line_length);
} else if (is_win(id, OSD1)) {
dispc_reg_out(OSD_OSDWIN1ADR, addr);
dispc_reg_out(OSD_OSDWIN1OFST, line_length);
}
}
static void set_win_enable(char *id, unsigned int on)
{
on = (on == 0) ? 0 : ~0;
if (is_win(id, VID0))
/* Turning off VID0 use due to field inversion issue */
dispc_reg_merge(OSD_VIDWINMD, 0, OSD_VIDWINMD_ACT0);
else if (is_win(id, VID1))
dispc_reg_merge(OSD_VIDWINMD, on, OSD_VIDWINMD_ACT1);
else if (is_win(id, OSD0))
dispc_reg_merge(OSD_OSDWIN0MD, on, OSD_OSDWIN0MD_OACT0);
else if (is_win(id, OSD1)) {
/* The OACT1 bit is applicable only if OSD1 is not used as
* the attribute window
*/
if (!(dispc_reg_in(OSD_OSDWIN1MD) & OSD_OSDWIN1MD_OASW))
dispc_reg_merge(OSD_OSDWIN1MD, on, OSD_OSDWIN1MD_OACT1);
}
}
static void set_win_mode(char *id)
{
if (is_win(id, VID0)) ;
else if (is_win(id, VID1)) {
if (dm->vid1->info.var.bits_per_pixel == 32)
dispc_reg_merge(OSD_MISCCT, ~0,
OSD_MISCCT_RGBWIN | OSD_MISCCT_RGBEN);
} else if (is_win(id, OSD0))
/* Set RGB565 mode */
dispc_reg_merge(OSD_OSDWIN0MD, OSD_OSDWIN0MD_RGB0E,
OSD_OSDWIN0MD_RGB0E);
else if (is_win(id, OSD1)) {
/* Set as attribute window */
dispc_reg_merge(OSD_OSDWIN1MD, OSD_OSDWIN1MD_OASW,
OSD_OSDWIN1MD_OASW);
}
}
/**
* davincifb_set_par - Optional function. Alters the hardware state.
* @info: frame buffer structure that represents a single frame buffer
*
* Using the fb_var_screeninfo in fb_info we set the resolution of the
* this particular framebuffer. This function alters the par AND the
* fb_fix_screeninfo stored in fb_info. It doesn't not alter var in
* fb_info since we are using that data. This means we depend on the
* data in var inside fb_info to be supported by the hardware.
* davincifb_check_var is always called before dmfb_set_par to ensure this.
* Again if you can't can't the resolution you don't need this function.
*
*/
static int davincifb_set_par(struct fb_info *info)
{
struct dm_win_info *w = (struct dm_win_info *)info->par;
struct fb_var_screeninfo *v = &info->var;
u32 start = 0, offset = 0;
info->fix.line_length = v->xres_virtual * v->bits_per_pixel / 8;
offset = v->yoffset * info->fix.line_length +
v->xoffset * v->bits_per_pixel / 8;
start = (u32) w->fb_base_phys + offset;
set_sdram_params(info->fix.id, start, info->fix.line_length);
set_interlaced(info->fix.id, 1);
set_win_position(info->fix.id,
x_pos(w), y_pos(w), v->xres, v->yres / 2);
set_win_mode(info->fix.id);
set_win_enable(info->fix.id, 1);
return (0);
}
/**
* davincifb_ioctl - handler for private ioctls.
*/
static int davincifb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct dm_win_info *w = (struct dm_win_info *)info->par;
void __user *argp = (void __user *)arg;
struct fb_fillrect rect;
struct zoom_params zoom;
long std = 0;
switch (cmd) {
case FBIO_WAITFORVSYNC:
/* This ioctl accepts an integer argument to specify a
* display. We only support one display, so we will
* simply ignore the argument.
*/
return (davincifb_wait_for_vsync(w));
break;
case FBIO_SETATTRIBUTE:
if (copy_from_user(&rect, argp, sizeof(rect)))
return -EFAULT;
return (davincifb_set_attr_blend(&rect));
break;
case FBIO_SETPOSX:
if (arg >= 0 && arg <= DISP_XRES) {
w->x = arg;
davincifb_check_var(&w->info.var, &w->info);
davincifb_set_par(&w->info);
return 0;
} else
return -EINVAL;
break;
case FBIO_SETPOSY:
if (arg >= 0 && arg <= DISP_YRES) {
w->y = arg;
davincifb_check_var(&w->info.var, &w->info);
davincifb_set_par(&w->info);
return 0;
} else
return -EINVAL;
break;
case FBIO_SETZOOM:
if (copy_from_user(&zoom, argp, sizeof(zoom)))
return -EFAULT;
if ((zoom.zoom_h == 2) || (zoom.zoom_h == 0)
|| (zoom.zoom_h == 1) || (zoom.zoom_v == 2)
|| (zoom.zoom_v == 0) || (zoom.zoom_v == 1)) {
set_zoom(zoom.window_id, zoom.zoom_h, zoom.zoom_v);
return 0;
} else {
return -EINVAL;
}
break;
case FBIO_GETSTD:
std = ((dmparams.output << 16) | (dmparams.format)); //(NTSC <<16) | (COPOSITE);
if (copy_to_user(argp, &std, sizeof(u_int32_t)))
return -EFAULT;
return 0;
break;
}
return (-EINVAL);
}
/**
* davincifb_setcolreg - Optional function. Sets a color register.
* @regno: Which register in the CLUT we are programming
* @red: The red value which can be up to 16 bits wide
* @green: The green value which can be up to 16 bits wide
* @blue: The blue value which can be up to 16 bits wide.
* @transp: If supported the alpha value which can be up to 16 bits wide.
* @info: frame buffer info structure
*
* Set a single color register. The values supplied have a 16 bit
* magnitude which needs to be scaled in this function for the hardware.
* Things to take into consideration are how many color registers, if
* any, are supported with the current color visual. With truecolor mode
* no color palettes are supported. Here a psuedo palette is created
* which we store the value in pseudo_palette in struct fb_info. For
* pseudocolor mode we have a limited color palette. To deal with this
* we can program what color is displayed for a particular pixel value.
* DirectColor is similar in that we can program each color field. If
* we have a static colormap we don't need to implement this function.
*
* Returns negative errno on error, or zero on success.
*/
static int davincifb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
/* only pseudo-palette (16 bpp) allowed */
if (regno >= 16) /* maximum number of palette entries */
return (1);
if (info->var.grayscale) {
/* grayscale = 0.30*R + 0.59*G + 0.11*B */
red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
}
/* Truecolor has hardware-independent 16-entry pseudo-palette */
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
u32 v;
if (regno >= 16)
return (1);
red >>= (16 - info->var.red.length);
green >>= (16 - info->var.green.length);
blue >>= (16 - info->var.blue.length);
v = (red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset);
switch (info->var.bits_per_pixel) {
case 16:
((u16 *) (info->pseudo_palette))[regno] = v;
break;
default:
return (1);
}
return (0);
}
return (0);
}
/**
* davincifb_pan_display - NOT a required function. Pans the display.
* @var: frame buffer variable screen structure
* @info: frame buffer structure that represents a single frame buffer
*
* Pan (or wrap, depending on the `vmode' field) the display using the
* `xoffset' and `yoffset' fields of the `var' structure.
* If the values don't fit, return -EINVAL.
*
* Returns negative errno on error, or zero on success.
*/
static int davincifb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct dm_win_info *w = (struct dm_win_info *)info->par;
u32 start = 0, offset = 0;
if (var->xoffset > var->xres_virtual - var->xres)
return (-EINVAL);
if (var->yoffset > var->yres_virtual - var->yres)
return (-EINVAL);
if ((var->xres_virtual * var->bits_per_pixel / 8) % 32)
return (-EINVAL);
offset = var->yoffset * info->fix.line_length +
var->xoffset * var->bits_per_pixel / 8;
start = (u32) w->fb_base_phys + offset;
if ((dispc_reg_in(VENC_VSTAT) & 0x00000010)==0x10)
set_sdram_params(info->fix.id, start, info->fix.line_length);
else
w->sdram_address = start;
return (0);
}
/**
* davincifb_blank - NOT a required function. Blanks the display.
* @blank_mode: the blank mode we want.
* @info: frame buffer structure that represents a single frame buffer
*
* Blank the screen if blank_mode != 0, else unblank. Return 0 if
* blanking succeeded, != 0 if un-/blanking failed due to e.g. a
* video mode which doesn't support it. Implements VESA suspend
* and powerdown modes on hardware that supports disabling hsync/vsync:
* blank_mode == 2: suspend vsync
* blank_mode == 3: suspend hsync
* blank_mode == 4: powerdown
*
* Returns negative errno on error, or zero on success.
*
*/
static int davincifb_blank(int blank_mode, struct fb_info *info)
{
return 0;
}
static int parse_win_params(char *wp,
int *xres, int *yres, int *xpos, int *ypos)
{
char *s;
if ((s = strsep(&wp, "x")) == NULL)
return -EINVAL;
*xres = simple_strtoul(s, NULL, 0);
if ((s = strsep(&wp, "@")) == NULL)
return -EINVAL;
*yres = simple_strtoul(s, NULL, 0);
if ((s = strsep(&wp, ",")) == NULL)
return -EINVAL;
*xpos = simple_strtoul(s, NULL, 0);
if ((s = strsep(&wp, ":")) == NULL)
return -EINVAL;
*ypos = simple_strtoul(s, NULL, 0);
return 0;
}
/*
* Pass boot-time options by adding the following string to the boot params:
* video=davincifb:[option[:option]]
* Valid options:
* output=[lcd|ntsc|pal]
* format=[composite|s-video|component|rgb]
* vid0=[off|MxN@X,Y]
* vid1=[off|MxN@X,Y]
* osd0=[off|MxN@X,Y]
* osd1=[off|MxN@X,Y]
* MxN specify the window resolution (displayed size)
* X,Y specify the window position
* M, N, X, Y are integers
* M, X should be multiples of 16
*/
#ifndef MODULE
int __init davincifb_setup(char *options)
{
char *this_opt;
u32 xres, yres, xpos, ypos;
int format_yres = 480;
pr_debug("davincifb: Options \"%s\"\n", options);
if (!options || !*options)
return 0;
while ((this_opt = strsep(&options, ":")) != NULL) {
if (!*this_opt)
continue;
if (!strncmp(this_opt, "output=", 7)) {
if (!strncmp(this_opt + 7, "lcd", 3)) {
dmparams.output = LCD;
dmparams.format = 0;
} else if (!strncmp(this_opt + 7, "ntsc", 4))
dmparams.output = NTSC;
else if (!strncmp(this_opt + 7, "pal", 3))
dmparams.output = PAL;
} else if (!strncmp(this_opt, "format=", 7)) {
if (dmparams.output == LCD)
continue;
if (!strncmp(this_opt + 7, "composite", 9))
dmparams.format = COMPOSITE;
else if (!strncmp(this_opt + 7, "s-video", 7))
dmparams.format = SVIDEO;
else if (!strncmp(this_opt + 7, "component", 9))
dmparams.format = COMPONENT;
else if (!strncmp(this_opt + 7, "rgb", 3))
dmparams.format = RGB;
} else if (!strncmp(this_opt, "vid0=", 5)) {
if (!strncmp(this_opt + 5, "off", 3))
dmparams.windows &= ~(1 << VID0);
else if (!parse_win_params(this_opt + 5,
&xres, &yres, &xpos,
&ypos)) {
dmparams.vid0_xres = xres;
dmparams.vid0_yres = yres;
dmparams.vid0_xpos = xpos;
dmparams.vid0_ypos = ypos;
}
} else if (!strncmp(this_opt, "vid1=", 5)) {
if (!strncmp(this_opt + 5, "off", 3))
dmparams.windows &= ~(1 << VID1);
else if (!parse_win_params(this_opt + 5,
&xres, &yres, &xpos,
&ypos)) {
dmparams.vid1_xres = xres;
dmparams.vid1_yres = yres;
dmparams.vid1_xpos = xpos;
dmparams.vid1_ypos = ypos;
}
} else if (!strncmp(this_opt, "osd0=", 5)) {
if (!strncmp(this_opt + 5, "off", 3))
dmparams.windows &= ~(1 << OSD0);
else if (!parse_win_params(this_opt + 5,
&xres, &yres, &xpos,
&ypos)) {
dmparams.osd0_xres = xres;
dmparams.osd0_yres = yres;
dmparams.osd0_xpos = xpos;
dmparams.osd0_ypos = ypos;
}
} else if (!strncmp(this_opt, "osd1=", 5)) {
if (!strncmp(this_opt + 5, "off", 3))
dmparams.windows &= ~(1 << OSD1);
else if (!parse_win_params(this_opt + 5,
&xres, &yres, &xpos,
&ypos)) {
dmparams.osd1_xres = xres;
dmparams.osd1_yres = yres;
dmparams.osd1_xpos = xpos;
dmparams.osd1_ypos = ypos;
}
}
}
printk(KERN_INFO "DaVinci: "
"Output on %s%s, Enabled windows: %s %s %s %s\n",
(dmparams.output == LCD) ? "LCD" :
(dmparams.output == NTSC) ? "NTSC" :
(dmparams.output == PAL) ? "PAL" : "unknown device!",
(dmparams.format == 0) ? "" :
(dmparams.format == COMPOSITE) ? " in COMPOSITE format" :
(dmparams.format == SVIDEO) ? " in SVIDEO format" :
(dmparams.format == COMPONENT) ? " in COMPONENT format" :
(dmparams.format == RGB) ? " in RGB format" : "",
(dmparams.windows & (1 << VID0)) ? "Video0" : "",
(dmparams.windows & (1 << VID1)) ? "Video1" : "",
(dmparams.windows & (1 << OSD0)) ? "OSD0" : "",
(dmparams.windows & (1 << OSD1)) ? "OSD1" : "");
if (dmparams.output == NTSC) {
format_yres = 480;
} else if (dmparams.output == PAL) {
format_yres = 576;
} else {
printk(KERN_INFO
"DaVinci:invalid format..defaulting width to 480\n");
}
dmparams.osd0_yres = osd0_default_var.yres = format_yres;
dmparams.osd1_yres = osd1_default_var.yres = format_yres;
dmparams.vid0_yres = vid0_default_var.yres = format_yres;
dmparams.vid1_yres = vid1_default_var.yres = format_yres;
osd0_default_var.yres_virtual = format_yres * DOUBLE_BUF;
osd1_default_var.yres_virtual = format_yres * DOUBLE_BUF;
vid0_default_var.yres_virtual = format_yres * TRIPLE_BUF;
vid1_default_var.yres_virtual = format_yres * TRIPLE_BUF;
if (dmparams.windows & (1 << VID0))
printk(KERN_INFO "Setting Video0 size %dx%d, "
"position (%d,%d)\n",
dmparams.vid0_xres, dmparams.vid0_yres,
dmparams.vid0_xpos, dmparams.vid0_ypos);
if (dmparams.windows & (1 << VID1))
printk(KERN_INFO "Setting Video1 size %dx%d, "
"position (%d,%d)\n",
dmparams.vid1_xres, dmparams.vid1_yres,
dmparams.vid1_xpos, dmparams.vid1_ypos);
if (dmparams.windows & (1 << OSD0))
printk(KERN_INFO "Setting OSD0 size %dx%d, "
"position (%d,%d)\n",
dmparams.osd0_xres, dmparams.osd0_yres,
dmparams.osd0_xpos, dmparams.osd0_ypos);
if (dmparams.windows & (1 << OSD1))
printk(KERN_INFO "Setting OSD1 size %dx%d, "
"position (%d,%d)\n",
dmparams.osd1_xres, dmparams.osd1_yres,
dmparams.osd1_xpos, dmparams.osd1_ypos);
return (0);
}
#endif
static int mem_release(struct dm_win_info *w)
{
if (!w->alloc_fb_mem) {
iounmap((void *)w->fb_base);
release_mem_region(w->fb_base_phys, w->fb_size);
} else
dma_free_coherent(NULL, w->fb_size, (void *)w->fb_base,
w->fb_base_phys);
kfree(w);
return (0);
}
static int mem_alloc(struct dm_win_info **win, dma_addr_t fb_base_phys,
unsigned long fb_size, char *fbname)
{
struct dm_win_info *w;
struct device *dev = dm->dev;
w = kmalloc(sizeof(struct dm_win_info), GFP_KERNEL);
if (!w) {
dev_err(dev, "%s: could not allocate memory\n", fbname);
return (-ENOMEM);
}
memset(w, 0, sizeof(struct dm_win_info));
w->fb_base_phys = fb_base_phys;
w->fb_size = fb_size;
/* A null base address indicates that the framebuffer memory should be
* dynamically allocated.
*/
if (!w->fb_base_phys)
w->alloc_fb_mem = 1;
if (!w->alloc_fb_mem) {
if (!request_mem_region(w->fb_base_phys, w->fb_size, fbname)) {
dev_err(dev, "%s: cannot reserve FB region\n", fbname);
goto free_par;
}
w->fb_base =
(unsigned long)ioremap(w->fb_base_phys, w->fb_size);
if (!w->fb_base) {
dev_err(dev, "%s: cannot map framebuffer\n", fbname);
goto release_fb;
}
} else {
/* allocate coherent memory for the framebuffer */
w->fb_base = (unsigned long)dma_alloc_coherent(dev,
w->fb_size,
&w->fb_base_phys,
GFP_KERNEL |
GFP_DMA);
if (!w->fb_base) {
dev_err(dev, "%s: cannot allocate framebuffer\n",
fbname);
goto free_par;
}
dev_dbg(dev, "Framebuffer allocated at 0x%x "
"mapped to 0x%x, size %dk\n",
(unsigned)w->fb_base_phys, (unsigned)w->fb_base,
(unsigned)w->fb_size / 1024);
}
*win = w;
return (0);
release_fb:
if (!w->alloc_fb_mem)
release_mem_region(w->fb_base_phys, w->fb_size);
free_par:
kfree(w);
return (-ENOMEM);
}
static struct fb_info *init_fb_info(struct dm_win_info *w,
struct fb_var_screeninfo *var, char *id)
{
struct fb_info *info = &(w->info);
struct dm_info *dm = w->dm;
/* initialize the fb_info structure */
info->flags = FBINFO_DEFAULT;
info->fbops = &davincifb_ops;
info->screen_base = (char *)(w->fb_base);
info->pseudo_palette = w->pseudo_palette;
info->par = w;
/* Initialize variable screeninfo.
* The variable screeninfo can be directly specified by the user
* via an ioctl.
*/
memcpy(&info->var, var, sizeof(info->var));
info->var.activate = FB_ACTIVATE_NOW;
/* Initialize fixed screeninfo.
* The fixed screeninfo cannot be directly specified by the user, but
* it may change to reflect changes to the var info.
*/
strlcpy(info->fix.id, id, sizeof(info->fix.id));
info->fix.smem_start = w->fb_base_phys;
info->fix.line_length =
(info->var.xres_virtual * info->var.bits_per_pixel) / 8;
info->fix.smem_len = w->fb_size;
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = (info->var.bits_per_pixel <= 8) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
info->fix.xpanstep = 0;
info->fix.ypanstep = 1;
info->fix.ywrapstep = 0;
info->fix.type_aux = 0;
info->fix.mmio_start = dm->mmio_base_phys;
info->fix.mmio_len = dm->mmio_size;
info->fix.accel = FB_ACCEL_NONE;
w->sdram_address = 0;
return info;
}
static void davincifb_ntsc_composite_config(int on)
{
if (on) {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
/* Enable Composite output and start video encoder */
dispc_reg_out(VENC_VMOD, (VENC_VMOD_VIE | VENC_VMOD_VENC));
/* Set REC656 Mode */
dispc_reg_out(VENC_YCCCTL, 0x1);
/* Enable output mode and NTSC */
dispc_reg_out(VENC_VMOD, 0x1003);
/* Enable all DACs */
dispc_reg_out(VENC_DACTST, 0);
} else {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
}
}
static void davincifb_ntsc_svideo_config(int on)
{
if (on) {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
/* Enable Composite output and start video encoder */
dispc_reg_out(VENC_VMOD, (VENC_VMOD_VIE | VENC_VMOD_VENC));
/* Set REC656 Mode */
dispc_reg_out(VENC_YCCCTL, 0x1);
/* Enable output mode and NTSC */
dispc_reg_out(VENC_VMOD, 0x1003);
/* Enable S-Video Output; DAC B: S-Video Y, DAC C: S-Video C */
dispc_reg_out(VENC_DACSEL, 0x210);
/* Enable all DACs */
dispc_reg_out(VENC_DACTST, 0);
} else {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
}
}
static void davincifb_ntsc_component_config(int on)
{
if (on) {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
/* Enable Composite output and start video encoder */
dispc_reg_out(VENC_VMOD, (VENC_VMOD_VIE | VENC_VMOD_VENC));
/* Set REC656 Mode */
dispc_reg_out(VENC_YCCCTL, 0x1);
/* Enable output mode and NTSC */
dispc_reg_out(VENC_VMOD, 0x1003);
/* Enable Component output; DAC A: Y, DAC B: Pb, DAC C: Pr */
dispc_reg_out(VENC_DACSEL, 0x543);
/* Enable all DACs */
dispc_reg_out(VENC_DACTST, 0);
} else {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
}
}
static void davincifb_pal_composite_config(int on)
{
if (on) {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
/* Enable Composite output and start video encoder */
dispc_reg_out(VENC_VMOD, (VENC_VMOD_VIE | VENC_VMOD_VENC));
/* Set REC656 Mode */
dispc_reg_out(VENC_YCCCTL, 0x1);
/* Enable output mode and PAL */
dispc_reg_out(VENC_VMOD, 0x1043);
/* Enable all DACs */
dispc_reg_out(VENC_DACTST, 0);
} else {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
}
}
static void davincifb_pal_svideo_config(int on)
{
if (on) {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
/* Enable Composite output and start video encoder */
dispc_reg_out(VENC_VMOD, (VENC_VMOD_VIE | VENC_VMOD_VENC));
/* Set REC656 Mode */
dispc_reg_out(VENC_YCCCTL, 0x1);
/* Enable output mode and PAL */
dispc_reg_out(VENC_VMOD, 0x1043);
/* Enable S-Video Output; DAC B: S-Video Y, DAC C: S-Video C */
dispc_reg_out(VENC_DACSEL, 0x210);
/* Enable all DACs */
dispc_reg_out(VENC_DACTST, 0);
} else {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
}
}
static void davincifb_pal_component_config(int on)
{
if (on) {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
/* Enable Composite output and start video encoder */
dispc_reg_out(VENC_VMOD, (VENC_VMOD_VIE | VENC_VMOD_VENC));
/* Set REC656 Mode */
dispc_reg_out(VENC_YCCCTL, 0x1);
/* Enable output mode and PAL */
dispc_reg_out(VENC_VMOD, 0x1043);
/* Enable Component output; DAC A: Y, DAC B: Pb, DAC C: Pr */
dispc_reg_out(VENC_DACSEL, 0x543);
/* Enable all DACs */
dispc_reg_out(VENC_DACTST, 0);
} else {
/* Reset video encoder module */
dispc_reg_out(VENC_VMOD, 0);
}
}
static inline void fix_default_var(struct dm_win_info *w,
u32 xres, u32 yres, u32 xpos, u32 ypos,
int n_buf)
{
struct fb_var_screeninfo *v = &w->info.var;
v->xres = xres;
v->yres = yres;
v->xres_virtual = v->xres;
v->yres_virtual = v->yres * n_buf;
x_pos(w) = xpos;
y_pos(w) = ypos;
}
/*
* Cleanup
*/
static int davincifb_remove(struct platform_device *pdev)
{
free_irq(IRQ_VENCINT, &dm);
/* Cleanup all framebuffers */
if (dm->osd0) {
unregister_framebuffer(&dm->osd0->info);
mem_release(dm->osd0);
}
if (dm->osd1) {
unregister_framebuffer(&dm->osd1->info);
mem_release(dm->osd1);
}
if (dm->vid0) {
unregister_framebuffer(&dm->vid0->info);
mem_release(dm->vid0);
}
if (dm->vid1) {
unregister_framebuffer(&dm->vid1->info);
mem_release(dm->vid1);
}
/* Turn OFF the output device */
dm->output_device_config(0);
if (dm->mmio_base)
iounmap((void *)dm->mmio_base);
release_mem_region(dm->mmio_base_phys, dm->mmio_size);
return 0;
}
/*
* Initialization
*/
static int davincifb_probe(struct platform_device *pdev)
{
struct fb_info *info;
if (dmparams.windows == 0)
return 0; /* user disabled all windows through bootargs */
dm->dev = &pdev->dev;
dm->mmio_base_phys = OSD_REG_BASE;
dm->mmio_size = OSD_REG_SIZE;
if (!request_mem_region
(dm->mmio_base_phys, dm->mmio_size, MODULE_NAME)) {
dev_err(dm->dev, ": cannot reserve MMIO region\n");
return (-ENODEV);
}
/* map the regions */
dm->mmio_base =
(unsigned long)ioremap(dm->mmio_base_phys, dm->mmio_size);
if (!dm->mmio_base) {
dev_err(dm->dev, ": cannot map MMIO\n");
goto release_mmio;
}
/* initialize the vsync wait queue */
init_waitqueue_head(&dm->vsync_wait);
dm->timeout = HZ / 5;
if ((dmparams.output == NTSC) && (dmparams.format == COMPOSITE))
dm->output_device_config = davincifb_ntsc_composite_config;
else if ((dmparams.output == NTSC) && (dmparams.format == SVIDEO))
dm->output_device_config = davincifb_ntsc_svideo_config;
else if ((dmparams.output == NTSC) && (dmparams.format == COMPONENT))
dm->output_device_config = davincifb_ntsc_component_config;
else if ((dmparams.output == PAL) && (dmparams.format == COMPOSITE))
dm->output_device_config = davincifb_pal_composite_config;
else if ((dmparams.output == PAL) && (dmparams.format == SVIDEO))
dm->output_device_config = davincifb_pal_svideo_config;
else if ((dmparams.output == PAL) && (dmparams.format == COMPONENT))
dm->output_device_config = davincifb_pal_component_config;
/* Add support for other displays here */
else {
printk(KERN_WARNING "Unsupported output device!\n");
dm->output_device_config = NULL;
}
printk("Setting Up Clocks for DM420 OSD\n");
/* Initialize the VPSS Clock Control register */
dispc_reg_out(VPSS_CLKCTL, 0x18);
/* Set Base Pixel X and Base Pixel Y */
dispc_reg_out(OSD_BASEPX, BASEX);
dispc_reg_out(OSD_BASEPY, BASEY);
/* Reset OSD registers to default. */
dispc_reg_out(OSD_MODE, 0);
dispc_reg_out(OSD_OSDWIN0MD, 0);
/* Set blue background color */
set_bg_color(0, 162);
/* Field Inversion Workaround */
dispc_reg_out(OSD_MODE, 0x200);
/* Setup VID0 framebuffer */
if (!(dmparams.windows & (1 << VID0))) {
printk(KERN_WARNING "No video/osd windows will be enabled "
"because Video0 is disabled\n");
return 0; /* background will still be shown */
}
/* Setup VID0 framebuffer */
if (!mem_alloc(&dm->vid0, VID0_FB_PHY, VID0_FB_SIZE, VID0_FBNAME)) {
dm->vid0->dm = dm;
fix_default_var(dm->vid0,
dmparams.vid0_xres, dmparams.vid0_yres,
dmparams.vid0_xpos, dmparams.vid0_ypos,
TRIPLE_BUF);
info = init_fb_info(dm->vid0, &vid0_default_var, VID0_FBNAME);
if (davincifb_check_var(&info->var, info)) {
dev_err(dm->dev, ": invalid default video mode\n");
goto exit;
}
memset((void *)dm->vid0->fb_base, 0x88, dm->vid0->fb_size);
} else
goto exit;
/* Setup OSD0 framebuffer */
if ((dmparams.windows & (1 << OSD0)) &&
(!mem_alloc(&dm->osd0, OSD0_FB_PHY, OSD0_FB_SIZE, OSD0_FBNAME))) {
dm->osd0->dm = dm;
fix_default_var(dm->osd0,
dmparams.osd0_xres, dmparams.osd0_yres,
dmparams.osd0_xpos, dmparams.osd0_ypos,
DOUBLE_BUF);
info = init_fb_info(dm->osd0, &osd0_default_var, OSD0_FBNAME);
if (davincifb_check_var(&info->var, info)) {
dev_err(dm->dev, ": invalid default video mode\n");
mem_release(dm->osd0);
} else
memset((void *)dm->osd0->fb_base, 0, dm->osd0->fb_size);
}
/* Setup OSD1 framebuffer */
if ((dmparams.windows & (1 << OSD1)) &&
(!mem_alloc(&dm->osd1, OSD1_FB_PHY, OSD1_FB_SIZE, OSD1_FBNAME))) {
dm->osd1->dm = dm;
fix_default_var(dm->osd1,
dmparams.osd1_xres, dmparams.osd1_yres,
dmparams.osd1_xpos, dmparams.osd1_ypos,
DOUBLE_BUF);
info = init_fb_info(dm->osd1, &osd1_default_var, OSD1_FBNAME);
if (davincifb_check_var(&info->var, info)) {
dev_err(dm->dev, ": invalid default video mode\n");
mem_release(dm->osd1);
} else
/* Set blend factor to show OSD windows */
memset((void *)dm->osd1->fb_base, 0xff,
dm->osd1->fb_size);
}
/* Setup VID1 framebuffer */
if ((dmparams.windows & (1 << VID1)) &&
(!mem_alloc(&dm->vid1, VID1_FB_PHY, VID1_FB_SIZE, VID1_FBNAME))) {
dm->vid1->dm = dm;
fix_default_var(dm->vid1,
dmparams.vid1_xres, dmparams.vid1_yres,
dmparams.vid1_xpos, dmparams.vid1_ypos,
TRIPLE_BUF);
info = init_fb_info(dm->vid1, &vid1_default_var, VID1_FBNAME);
if (davincifb_check_var(&info->var, info)) {
dev_err(dm->dev,
VID1_FBNAME ": invalid default video mode\n");
mem_release(dm->vid1);
} else
memset((void *)dm->vid1->fb_base, 0x88,
dm->vid1->fb_size);
}
/* Register OSD0 framebuffer */
if (dm->osd0) {
info = &dm->osd0->info;
if (register_framebuffer(info) < 0) {
dev_err(dm->dev, OSD0_FBNAME
"Unable to register OSD0 framebuffer\n");
mem_release(dm->osd0);
} else {
printk(KERN_INFO "fb%d: %s frame buffer device\n",
info->node, info->fix.id);
davincifb_set_par(info);
}
}
/* Register VID0 framebuffer */
info = &dm->vid0->info;
if (register_framebuffer(info) < 0) {
dev_err(dm->dev,
VID0_FBNAME "Unable to register VID0 framebuffer\n");
goto exit;
} else {
printk(KERN_INFO "fb%d: %s frame buffer device\n",
info->node, info->fix.id);
davincifb_set_par(info);
}
/* Register OSD1 framebuffer */
if (dm->osd1) {
info = &dm->osd1->info;
if (register_framebuffer(info) < 0) {
dev_err(dm->dev, OSD1_FBNAME
"Unable to register OSD1 framebuffer\n");
mem_release(dm->osd1);
} else {
printk(KERN_INFO "fb%d: %s frame buffer device\n",
info->node, info->fix.id);
davincifb_set_par(info);
}
}
/* Register VID1 framebuffer */
if (dm->vid1) {
info = &dm->vid1->info;
if (register_framebuffer(info) < 0) {
mem_release(dm->vid1);
dev_err(dm->dev, VID1_FBNAME
"Unable to register VID1 framebuffer\n");
mem_release(dm->vid1);
} else {
printk(KERN_INFO "fb%d: %s frame buffer device\n",
info->node, info->fix.id);
davincifb_set_par(info);
}
}
/* install our interrupt service routine */
if (request_irq(IRQ_VENCINT, davincifb_isr, IRQF_SHARED, MODULE_NAME,
dm)) {
dev_err(dm->dev, MODULE_NAME
": could not install interrupt service routine\n");
goto exit;
}
/* Turn ON the output device */
dm->output_device_config(1);
return (0);
exit:
davincifb_remove(pdev);
iounmap((void *)dm->mmio_base);
release_mmio:
release_mem_region(dm->mmio_base_phys, dm->mmio_size);
return (-ENODEV);
}
/* ------------------------------------------------------------------------- */
/*
* Frame buffer operations
*/
static struct fb_ops davincifb_ops = {
.owner = THIS_MODULE,
.fb_check_var = davincifb_check_var,
.fb_set_par = davincifb_set_par,
.fb_setcolreg = davincifb_setcolreg,
.fb_blank = davincifb_blank,
.fb_pan_display = davincifb_pan_display,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_rotate = NULL,
.fb_sync = NULL,
.fb_ioctl = davincifb_ioctl,
};
static struct platform_driver davincifb_driver = {
.probe = davincifb_probe,
.remove = davincifb_remove,
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
},
};
/* Register both the driver and the device */
int __init davincifb_init(void)
{
#ifndef MODULE
/* boot-line options */
/* handle options for "dm64xxfb" for backwards compatability */
char *option;
char *names[] = { "davincifb", "dm64xxfb" };
int i, num_names = 2, done = 0;
for (i = 0; i < num_names && !done; i++) {
if (fb_get_options(names[i], &option)) {
printk(MODULE_NAME
": Disabled on command-line.\n");
return -ENODEV;
} else if (option) {
davincifb_setup(option);
done = 1;
}
}
#endif
/* Register the driver with LDM */
if (platform_driver_register(&davincifb_driver)) {
pr_debug("failed to register omapfb driver\n");
return -ENODEV;
}
return 0;
}
static void __exit davincifb_cleanup(void)
{
platform_driver_unregister(&davincifb_driver);
}
module_init(davincifb_init);
module_exit(davincifb_cleanup);
MODULE_DESCRIPTION("Framebuffer driver for TI DaVinci");
MODULE_AUTHOR("Texas Instruments");
MODULE_LICENSE("GPL");
......@@ -25,6 +25,7 @@
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/clk.h>
#define MODULE_NAME "DAVINCI-WDT: "
......@@ -69,6 +70,7 @@ static unsigned long wdt_status;
static struct resource *wdt_mem;
static void __iomem *wdt_base;
struct clk *wdt_clk;
static void wdt_service(void)
{
......@@ -86,6 +88,10 @@ static void wdt_enable(void)
{
u32 tgcr;
u32 timer_margin;
u32 wdt_freq;
BUG_ON(!wdt_clk);
wdt_freq = clk_get_rate(wdt_clk);
spin_lock(&io_lock);
......@@ -99,9 +105,9 @@ static void wdt_enable(void)
iowrite32(0, wdt_base + TIM12);
iowrite32(0, wdt_base + TIM34);
/* set timeout period */
timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) & 0xffffffff);
timer_margin = (((u64)heartbeat * wdt_freq) & 0xffffffff);
iowrite32(timer_margin, wdt_base + PRD12);
timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) >> 32);
timer_margin = (((u64)heartbeat * wdt_freq) >> 32);
iowrite32(timer_margin, wdt_base + PRD34);
/* enable run continuously */
iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR);
......@@ -199,6 +205,11 @@ static int davinci_wdt_probe(struct platform_device *pdev)
struct resource *res;
struct device *dev = &pdev->dev;
wdt_clk = clk_get(dev, NULL);
if (WARN_ON(!wdt_clk))
return -ENODEV;
clk_enable(wdt_clk);
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
heartbeat = DEFAULT_HEARTBEAT;
......@@ -245,6 +256,12 @@ static int davinci_wdt_remove(struct platform_device *pdev)
kfree(wdt_mem);
wdt_mem = NULL;
}
if (wdt_clk) {
clk_disable(wdt_clk);
clk_put(wdt_clk);
}
return 0;
}
......
......@@ -121,6 +121,13 @@ typedef enum {
NAND_ECC_SOFT,
NAND_ECC_HW,
NAND_ECC_HW_SYNDROME,
#ifdef CONFIG_NAND_FLASH_HW_ECC
NAND_ECC_HW3_256,
NAND_ECC_HW3_512,
NAND_ECC_HW6_512,
NAND_ECC_HW8_512,
NAND_ECC_HW12_2048,
#endif
} nand_ecc_modes_t;
/*
......
/*
*
* Copyright (C) 2006 Texas Instruments Inc
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* ccdc_davinci.h */
#ifndef CCDC_DAVINCI_H
#define CCDC_DAVINCI_H
#include <linux/types.h>
#ifdef __KERNEL__
#include <asm/arch/hardware.h>
#include <asm/io.h>
#endif
#include <linux/videodev.h>
typedef enum ccdc_pixfmt {
CCDC_PIXFMT_RAW = 0,
CCDC_PIXFMT_YCBCR_16BIT = 1,
CCDC_PIXFMT_YCBCR_8BIT = 2
} ccdc_pixfmt;
typedef enum ccdc_frmfmt {
CCDC_FRMFMT_PROGRESSIVE = 0,
CCDC_FRMFMT_INTERLACED = 1
} ccdc_frmfmt;
typedef enum ccdc_pinpol {
CCDC_PINPOL_POSITIVE = 0,
CCDC_PINPOL_NEGATIVE = 1
} ccdc_pinpol;
/* PIXEL ORDER IN MEMORY from LSB to MSB */
/* only applicable for 8-bit input mode */
typedef enum ccdc_pixorder {
CCDC_PIXORDER_CBYCRY = 1,
CCDC_PIXORDER_YCBYCR = 0
} ccdc_pixorder;
typedef enum ccdc_buftype {
CCDC_BUFTYPE_FLD_INTERLEAVED,
CCDC_BUFTYPE_FLD_SEPARATED
} ccdc_buftype;
typedef struct v4l2_rect ccdc_imgwin;
typedef struct ccdc_params_ycbcr {
ccdc_pixfmt pix_fmt; /* pixel format */
ccdc_frmfmt frm_fmt; /* progressive or interlaced frame */
ccdc_imgwin win; /* video window */
ccdc_pinpol fid_pol; /* field id polarity */
ccdc_pinpol vd_pol; /* vertical sync polarity */
ccdc_pinpol hd_pol; /* horizontal sync polarity */
int bt656_enable; /* enable BT.656 embedded sync mode */
ccdc_pixorder pix_order;/* cb:y:cr:y or y:cb:y:cr in memory */
ccdc_buftype buf_type; /* interleaved or separated fields */
} ccdc_params_ycbcr;
#ifdef __KERNEL__
/**************************************************************************\
* Register OFFSET Definitions
\**************************************************************************/
#define PID 0x0
#define PCR 0x4
#define SYN_MODE 0x8
#define HD_VD_WID 0xc
#define PIX_LINES 0x10
#define HORZ_INFO 0x14
#define VERT_START 0x18
#define VERT_LINES 0x1c
#define CULLING 0x20
#define HSIZE_OFF 0x24
#define SDOFST 0x28
#define SDR_ADDR 0x2c
#define CLAMP 0x30
#define DCSUB 0x34
#define COLPTN 0x38
#define BLKCMP 0x3c
#define FPC 0x40
#define FPC_ADDR 0x44
#define VDINT 0x48
#define ALAW 0x4c
#define REC656IF 0x50
#define CCDCFG 0x54
#define FMTCFG 0x58
#define FMT_HORZ 0x5c
#define FMT_VERT 0x50
#define FMT_ADDR0 0x64
#define FMT_ADDR1 0x68
#define FMT_ADDR2 0x6c
#define FMT_ADDR3 0x70
#define FMT_ADDR4 0x74
#define FMT_ADDR5 0x78
#define FMT_ADDR6 0x7c
#define FMT_ADDR7 0x80
#define PRGEVEN_0 0x84
#define PRGEVEN_1 0x88
#define PRGODD_0 0x8c
#define PRGODD_1 0x90
#define VP_OUT 0x94
#define CCDC_IOBASE (0x01c70400)
#define regw(val, reg) davinci_writel(val, (reg)+CCDC_IOBASE)
#define regr(reg) davinci_readl((reg)+CCDC_IOBASE)
extern void ccdc_reset(void);
extern void ccdc_config_ycbcr(ccdc_params_ycbcr * params);
extern void ccdc_setwin(ccdc_params_ycbcr * params);
/* inline functions that must be fast because they are called frequently */
static inline void ccdc_enable(int flag)
{
regw(flag, PCR);
}
static inline void ccdc_setfbaddr(unsigned long paddr)
{
regw(paddr & 0xffffffe0, SDR_ADDR);
}
static inline int ccdc_getfid(void)
{
int fid = (regr(SYN_MODE) >> 15) & 0x1;
return fid;
}
#endif
#endif /* CCDC_DAVINCI_H */
/*
* Copyright (C) 2006 Texas Instruments Inc
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* davinci_vpfe.h */
#ifndef DAVINCI_VPFE_H
#define DAVINCI_VPFE_H
#ifdef __KERNEL__
#include <media/v4l2-dev.h>
#endif
#include <media/ccdc_davinci.h>
#include <media/tvp5146.h>
#define TRUE 1
#define FALSE 0
/* vpfe specific video standards */
#define VPFE_STD_625_50_SQP ((V4L2_STD_625_50)<<32)
#define VPFE_STD_525_60_SQP ((V4L2_STD_525_60)<<32)
#define VPFE_STD_AUTO ((v4l2_std_id)(0x1000000000000000ULL))
#define VPFE_STD_AUTO_SQP ((v4l2_std_id)(0x2000000000000000ULL))
#define VPFE_CMD_CONFIG_CCDC _IOW('V',BASE_VIDIOC_PRIVATE + 1,ccdc_params_ycbcr)
#define VPFE_CMD_LATEST_FRM_ONLY _IOW('V',BASE_VIDIOC_PRIVATE + 2,int)
#define VPFE_CMD_CONFIG_TVP5146 _IOW('V',BASE_VIDIOC_PRIVATE + 3,tvp5146_params)
/* settings for commonly used video formats */
#define VPFE_WIN_NTSC {0,0,720,480}
#define VPFE_WIN_PAL {0,0,720,576}
#define VPFE_WIN_NTSC_SP {0,0,640,480} /* ntsc square pixel */
#define VPFE_WIN_PAL_SP {0,0,768,576} /* pal square pixel */
#define VPFE_WIN_CIF {0,0,352,288}
#define VPFE_WIN_QCIF {0,0,176,144}
#define VPFE_WIN_QVGA {0,0,320,240}
#define VPFE_WIN_SIF {0,0,352,240}
#ifdef __KERNEL__
#include <media/video-buf.h>
#define VPFE_MAJOR_RELEASE 0
#define VPFE_MINOR_RELEASE 0
#define VPFE_BUILD 1
#define VPFE_VERSION_CODE \
(VPFE_MAJOR_RELEASE<<16) | (VPFE_MINOR_RELEASE<<8) | VPFE_BUILD
/* By default, the driver is setup for auto-swich mode */
#define VPFE_DEFAULT_STD VPFE_STD_AUTO
#define VPFE_PIXELASPECT_NTSC {11, 10}
#define VPFE_PIXELASPECT_PAL {54, 59}
#define VPFE_PIXELASPECT_NTSC_SP {1, 1}
#define VPFE_PIXELASPECT_PAL_SP {1, 1}
#define VPFE_PIXELASPECT_DEFAULT {1, 1}
#define VPFE_MAX_FRAME_WIDTH 768 /* account for PAL Square pixel mode */
#define VPFE_MAX_FRAME_HEIGHT 576 /* account for PAL */
/* 4:2:2 data */
#define VPFE_MAX_FBUF_SIZE (VPFE_MAX_FRAME_WIDTH*VPFE_MAX_FRAME_HEIGHT*2)
/* frame buffers allocate at driver initialization time */
#define VPFE_DEFNUM_FBUFS 3
#define VPFE_MAX_FBUF_ORDER \
get_order(roundup_pow_of_two(VPFE_MAX_FBUF_SIZE))
/* device object */
typedef struct vpfe_obj {
struct video_device *video_dev;
struct videobuf_queue bufqueue;/* queue with frame buffers */
struct list_head dma_queue;
u32 latest_only; /* indicate whether to return the most */
/* recent captured buffers only */
u32 usrs;
u32 io_usrs;
struct v4l2_prio_state prio;
v4l2_std_id std;
struct v4l2_rect vwin;
struct v4l2_rect bounds;
struct v4l2_fract pixelaspect;
spinlock_t irqlock;
struct semaphore lock;
enum v4l2_field field;
u32 pixelfmt;
u32 numbuffers;
u8* fbuffers[VIDEO_MAX_FRAME];
struct videobuf_buffer *curFrm;
struct videobuf_buffer *nextFrm;
int field_id;
int mode_changed;
int started;
int field_offset;
tvp5146_params tvp5146_params;
ccdc_params_ycbcr ccdc_params;
} vpfe_obj;
/* file handle */
typedef struct vpfe_fh {
struct vpfe_obj *dev;
int io_allowed;
enum v4l2_priority prio;
} vpfe_fh;
#endif
#endif /* DAVINCI_VPFE_H */
/*
*
*
* Copyright (C) 2006 Texas Instruments Inc
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* tvp5146.h file */
#ifndef TVP5146_H
#define TVP5146_H
#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif
#define TRUE 1
#define FALSE 0
/* analog muxing mode */
#define TVP5146_AMUX_COMPOSITE 0
#define TVP5146_AMUX_SVIDEO 1
typedef enum {
TVP5146_MODE_INV = -1,
TVP5146_MODE_AUTO = 0, /* autoswitch mode (default) */
TVP5146_MODE_NTSC = 1, /* (M, J) NTSC 525-line */
TVP5146_MODE_PAL = 2, /* (B, D, G, H, I, N) PAL */
TVP5146_MODE_PAL_M = 3, /* (M) PAL 525-line */
TVP5146_MODE_PAL_CN = 4, /* (Combination-N) PAL */
TVP5146_MODE_NTSC_443 = 5, /* NTSC 4.43 525-line */
TVP5146_MODE_SECAM = 6, /* SECAM */
TVP5146_MODE_PAL_60 = 7, /* PAL 60 525-line */
TVP5146_MODE_AUTO_SQP = 8, /* autoswitch mode (default) */
TVP5146_MODE_NTSC_SQP = 9, /* (M, J) NTSC 525-line */
TVP5146_MODE_PAL_SQP = 0xA, /* (B, D, G, H, I, N) PAL */
TVP5146_MODE_PAL_M_SQP = 0xB, /* (M) PAL 525-line */
TVP5146_MODE_PAL_CN_SQP = 0xC, /* (Combination-N) PAL */
TVP5146_MODE_NTSC_443_SQP = 0xD,/* NTSC 4.43 525-line */
TVP5146_MODE_SECAM_SQP = 0xE, /* SECAM */
TVP5146_MODE_PAL_60_SQP = 0xF, /* PAL 60 525-line */
} tvp5146_mode;
typedef struct {
tvp5146_mode mode;
int amuxmode;
int enablebt656sync;
} tvp5146_params;
#ifdef __KERNEL__
typedef struct {
int agc_enable;
tvp5146_mode video_std;
int brightness;
int contrast;
int saturation;
int hue;
int field_rate; /* 50 or 60 in Hz */
int lost_lock;
int csubc_lock;
int v_lock;
int h_lock;
} tvp5146_status;
typedef unsigned int tvp5146_cmd;
/* commands for setup the decoder */
#define TVP5146_SET_AMUXMODE 4
#define TVP5146_SET_BRIGHTNESS 5
#define TVP5146_SET_CONTRAST 6
#define TVP5146_SET_HUE 7
#define TVP5146_SET_SATURATION 8
#define TVP5146_SET_AGC 9
#define TVP5146_SET_VIDEOSTD 10
#define TVP5146_CLR_LOSTLOCK 11
#define TVP5146_CONFIG 12
#define TVP5146_RESET 13
#define TVP5146_POWERDOWN 14
#define TVP5146_GET_STATUS 15
#define TVP5146_GET_STD 16
#define TVP5146_I2C_ADDR (0xBA >> 1)
extern int tvp5146_ctrl(tvp5146_cmd cmd, void *arg);
#endif
#endif
/*
* include/sound/davincisound.h
*
* Copyright (C) 2006 Texas Instruments, Inc.
*
* This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* History:
* --------
* 2006-03-29 Sudhakar - Created
*/
#include <linux/soundcard.h>
#define SOUND_MIXER_MICBIAS _IOC_NR(SOUND_MIXER_PRIVATE1)
#define SOUND_MIXER_READ_MICBIAS _SIOR ('M', SOUND_MIXER_MICBIAS, int)
#define SOUND_MIXER_WRITE_MICBIAS SOUND_MIXER_PRIVATE1
/*
* include/video/davincifb.h
*
* Framebuffer driver for Texas Instruments DM644x display controller.
*
* Copyright (C) 2006 Texas Instruments, Inc.
* Rishi Bhattacharya <support@ti.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#ifndef _DAVINCIFB_H_
#define _DAVINCIFB_H_
#include <mach/io.h>
/* Base registers */
#define VPBE_REG_BASE 0x01c72780
#define VENC_REG_BASE 0x01c72400
#define OSD_REG_BASE 0x01c72600
#define OSD_REG_SIZE 0x00000180
/* VPBE Global Registers */
#define VPBE_PID (VPBE_BASE + 0x0)
#define VPBE_PCR (VPBE_BASE + 0x4)
/* VPSS Clock Control Register */
#define VPSS_CLKCTL 0x01c40044
/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */
#define VENC_VMOD (VENC_REG_BASE + 0x00)
#define VENC_VIDCTL (VENC_REG_BASE + 0x04)
#define VENC_VDPRO (VENC_REG_BASE + 0x08)
#define VENC_SYNCCTL (VENC_REG_BASE + 0x0C)
#define VENC_HSPLS (VENC_REG_BASE + 0x10)
#define VENC_VSPLS (VENC_REG_BASE + 0x14)
#define VENC_HINT (VENC_REG_BASE + 0x18)
#define VENC_HSTART (VENC_REG_BASE + 0x1C)
#define VENC_HVALID (VENC_REG_BASE + 0x20)
#define VENC_VINT (VENC_REG_BASE + 0x24)
#define VENC_VSTART (VENC_REG_BASE + 0x28)
#define VENC_VVALID (VENC_REG_BASE + 0x2C)
#define VENC_HSDLY (VENC_REG_BASE + 0x30)
#define VENC_VSDLY (VENC_REG_BASE + 0x34)
#define VENC_YCCCTL (VENC_REG_BASE + 0x38)
#define VENC_RGBCTL (VENC_REG_BASE + 0x3C)
#define VENC_RGBCLP (VENC_REG_BASE + 0x40)
#define VENC_LINECTL (VENC_REG_BASE + 0x44)
#define VENC_CULLLINE (VENC_REG_BASE + 0x48)
#define VENC_LCDOUT (VENC_REG_BASE + 0x4C)
#define VENC_BRTS (VENC_REG_BASE + 0x50)
#define VENC_BRTW (VENC_REG_BASE + 0x54)
#define VENC_ACCTL (VENC_REG_BASE + 0x58)
#define VENC_PWMP (VENC_REG_BASE + 0x5C)
#define VENC_PWMW (VENC_REG_BASE + 0x60)
#define VENC_DCLKCTL (VENC_REG_BASE + 0x64)
#define VENC_DCLKPTN0 (VENC_REG_BASE + 0x68)
#define VENC_DCLKPTN1 (VENC_REG_BASE + 0x6C)
#define VENC_DCLKPTN2 (VENC_REG_BASE + 0x70)
#define VENC_DCLKPTN3 (VENC_REG_BASE + 0x74)
#define VENC_DCLKPTN0A (VENC_REG_BASE + 0x78)
#define VENC_DCLKPTN1A (VENC_REG_BASE + 0x7C)
#define VENC_DCLKPTN2A (VENC_REG_BASE + 0x80)
#define VENC_DCLKPTN3A (VENC_REG_BASE + 0x84)
#define VENC_DCLKHS (VENC_REG_BASE + 0x88)
#define VENC_DCLKHSA (VENC_REG_BASE + 0x8C)
#define VENC_DCLKHR (VENC_REG_BASE + 0x90)
#define VENC_DCLKVS (VENC_REG_BASE + 0x94)
#define VENC_DCLKVR (VENC_REG_BASE + 0x98)
#define VENC_CAPCTL (VENC_REG_BASE + 0x9C)
#define VENC_CAPDO (VENC_REG_BASE + 0xA0)
#define VENC_CAPDE (VENC_REG_BASE + 0xA4)
#define VENC_ATR0 (VENC_REG_BASE + 0xA8)
#define VENC_ATR1 (VENC_REG_BASE + 0xAC)
#define VENC_ATR2 (VENC_REG_BASE + 0xB0)
#define VENC_EPSON_LCDCTL (VENC_REG_BASE + 0xB4)
#define VENC_CASIO_LCDCTL (VENC_REG_BASE + 0xB4)
#define VENC_UDISP_LCDCT (VENC_REG_BASE + 0xB4)
#define VENC_STN_LCDCT (VENC_REG_BASE + 0xB4)
#define VENC_VSTAT (VENC_REG_BASE + 0xB8)
#define VENC_RAMADR (VENC_REG_BASE + 0xBC)
#define VENC_RAMPORT (VENC_REG_BASE + 0xC0)
#define VENC_DACTST (VENC_REG_BASE + 0xC4)
#define VENC_YCOLVL (VENC_REG_BASE + 0xC8)
#define VENC_SCPROG (VENC_REG_BASE + 0xCC)
#define VENC_CVBS (VENC_REG_BASE + 0xDC)
#define VENC_CMPNT (VENC_REG_BASE + 0xE0)
#define VENC_ETMG0 (VENC_REG_BASE + 0xE4)
#define VENC_ETMG1 (VENC_REG_BASE + 0xE8)
#define VENC_ETMG2 (VENC_REG_BASE + 0xEC)
#define VENC_ETMG3 (VENC_REG_BASE + 0xF0)
#define VENC_DACSEL (VENC_REG_BASE + 0xF4)
#define VENC_ARGBX0 (VENC_REG_BASE + 0x100)
#define VENC_ARGBX1 (VENC_REG_BASE + 0x104)
#define VENC_ARGBX2 (VENC_REG_BASE + 0x108)
#define VENC_ARGBX3 (VENC_REG_BASE + 0x10C)
#define VENC_ARGBX4 (VENC_REG_BASE + 0x110)
#define VENC_DRGBX0 (VENC_REG_BASE + 0x114)
#define VENC_DRGBX1 (VENC_REG_BASE + 0x118)
#define VENC_DRGBX2 (VENC_REG_BASE + 0x11C)
#define VENC_DRGBX3 (VENC_REG_BASE + 0x120)
#define VENC_DRGBX4 (VENC_REG_BASE + 0x124)
#define VENC_VSTARTA (VENC_REG_BASE + 0x128)
#define VENC_OSDCLK0 (VENC_REG_BASE + 0x12C)
#define VENC_OSDCLK1 (VENC_REG_BASE + 0x130)
#define VENC_HVLDCL0 (VENC_REG_BASE + 0x134)
#define VENC_HVLDCL1 (VENC_REG_BASE + 0x138)
#define VENC_OSDHAD (VENC_REG_BASE + 0x13C)
#define VID0 0
#define VID1 1
#define OSD0 3
#define OSD1 4
/* VPBE On-Screen Display Subsystem Registers (OSD) */
#define OSD_MODE (OSD_REG_BASE + 0x00)
#define OSD_VIDWINMD (OSD_REG_BASE + 0x04)
#define OSD_OSDWIN0MD (OSD_REG_BASE + 0x08)
#define OSD_OSDWIN1MD (OSD_REG_BASE + 0x0C)
#define OSD_OSDATRMD (OSD_REG_BASE + 0x0C)
#define OSD_RECTCUR (OSD_REG_BASE + 0x10)
#define OSD_WINOFST(i) (OSD_REG_BASE + 0x18 + (i)*0x4)
#define OSD_VIDWIN0OFST (OSD_REG_BASE + 0x18)
#define OSD_VIDWIN1OFST (OSD_REG_BASE + 0x1C)
#define OSD_OSDWIN0OFST (OSD_REG_BASE + 0x20)
#define OSD_OSDWIN1OFST (OSD_REG_BASE + 0x24)
#define OSD_WINADR(i) (OSD_REG_BASE + 0x2C + (i)*0x4)
#define OSD_VIDWIN0ADR (OSD_REG_BASE + 0x2C)
#define OSD_VIDWIN1ADR (OSD_REG_BASE + 0x30)
#define OSD_OSDWIN0ADR (OSD_REG_BASE + 0x38)
#define OSD_OSDWIN1ADR (OSD_REG_BASE + 0x3C)
#define OSD_BASEPX (OSD_REG_BASE + 0x40)
#define OSD_BASEPY (OSD_REG_BASE + 0x44)
#define OSD_WINXP(i) (OSD_REG_BASE + 0x48 + (i)*0x10)
#define OSD_WINYP(i) (OSD_REG_BASE + 0x4C + (i)*0x10)
#define OSD_WINXL(i) (OSD_REG_BASE + 0x50 + (i)*0x10)
#define OSD_WINYL(i) (OSD_REG_BASE + 0x54 + (i)*0x10)
#define OSD_VIDWIN0XP (OSD_REG_BASE + 0x48)
#define OSD_VIDWIN0YP (OSD_REG_BASE + 0x4C)
#define OSD_VIDWIN0XL (OSD_REG_BASE + 0x50)
#define OSD_VIDWIN0YL (OSD_REG_BASE + 0x54)
#define OSD_VIDWIN1XP (OSD_REG_BASE + 0x58)
#define OSD_VIDWIN1YP (OSD_REG_BASE + 0x5C)
#define OSD_VIDWIN1XL (OSD_REG_BASE + 0x60)
#define OSD_VIDWIN1YL (OSD_REG_BASE + 0x64)
#define OSD_OSDWIN0XP (OSD_REG_BASE + 0x68)
#define OSD_OSDWIN0YP (OSD_REG_BASE + 0x6C)
#define OSD_OSDWIN0XL (OSD_REG_BASE + 0x70)
#define OSD_OSDWIN0YL (OSD_REG_BASE + 0x74)
#define OSD_OSDWIN1XP (OSD_REG_BASE + 0x78)
#define OSD_OSDWIN1YP (OSD_REG_BASE + 0x7C)
#define OSD_OSDWIN1XL (OSD_REG_BASE + 0x80)
#define OSD_OSDWIN1YL (OSD_REG_BASE + 0x84)
#define OSD_CURXP (OSD_REG_BASE + 0x88)
#define OSD_CURYP (OSD_REG_BASE + 0x8C)
#define OSD_CURXL (OSD_REG_BASE + 0x90)
#define OSD_CURYL (OSD_REG_BASE + 0x94)
#define OSD_W0BMP01 (OSD_REG_BASE + 0xA0)
#define OSD_W0BMP23 (OSD_REG_BASE + 0xA4)
#define OSD_W0BMP45 (OSD_REG_BASE + 0xA8)
#define OSD_W0BMP67 (OSD_REG_BASE + 0xAC)
#define OSD_W0BMP89 (OSD_REG_BASE + 0xB0)
#define OSD_W0BMPAB (OSD_REG_BASE + 0xB4)
#define OSD_W0BMPCD (OSD_REG_BASE + 0xB8)
#define OSD_W0BMPEF (OSD_REG_BASE + 0xBC)
#define OSD_W1BMP0 (OSD_REG_BASE + 0xC0)
#define OSD_W1BMP2 (OSD_REG_BASE + 0xC4)
#define OSD_W1BMP4 (OSD_REG_BASE + 0xC8)
#define OSD_W1BMP6 (OSD_REG_BASE + 0xCC)
#define OSD_W1BMP8 (OSD_REG_BASE + 0xD0)
#define OSD_W1BMPA (OSD_REG_BASE + 0xD4)
#define OSD_W1BMPC (OSD_REG_BASE + 0xD8)
#define OSD_W1BMPE (OSD_REG_BASE + 0xDC)
#define OSD_TI_TES (OSD_REG_BASE + 0xE0)
#define OSD_MISCCT (OSD_REG_BASE + 0xE8)
#define OSD_CLUTRAMYC (OSD_REG_BASE + 0xEC)
#define OSD_CLUTRAMC (OSD_REG_BASE + 0xF0)
#define OSD_TRANSPVA (OSD_REG_BASE + 0xF0)
#define OSD_PPVWIN0AD (OSD_REG_BASE + 0xFC)
/* bit definitions */
#define VPBE_PCR_VENC_DIV (1 << 1)
#define VPBE_PCR_CLK_OFF (1 << 0)
#define VENC_VMOD_VDMD_SHIFT 12
#define VENC_VMOD_VDMD_YCBCR16 0
#define VENC_VMOD_VDMD_YCBCR8 1
#define VENC_VMOD_VDMD_RGB666 2
#define VENC_VMOD_VDMD_RGB8 3
#define VENC_VMOD_VDMD_EPSON 4
#define VENC_VMOD_VDMD_CASIO 5
#define VENC_VMOD_VDMD_UDISPQVGA 6
#define VENC_VMOD_VDMD_STNLCD 7
#define VENC_VMOD_VDMD (7 << 12)
#define VENC_VMOD_ITLCL (1 << 11)
#define VENC_VMOD_ITLC (1 << 10)
#define VENC_VMOD_NSIT (1 << 9)
#define VENC_VMOD_HDMD (1 << 8)
#define VENC_VMOD_TVTYP (3 << 6)
#define VENC_VMOD_SLAVE (1 << 5)
#define VENC_VMOD_VMD (1 << 4)
#define VENC_VMOD_BLNK (1 << 3)
#define VENC_VMOD_VIE (1 << 1)
#define VENC_VMOD_VENC (1 << 0)
/* other VENC registers' bit positions not defined yet */
#define OSD_MODE_CS (1 << 15)
#define OSD_MODE_OVRSZ (1 << 14)
#define OSD_MODE_OHRSZ (1 << 13)
#define OSD_MODE_EF (1 << 12)
#define OSD_MODE_VVRSZ (1 << 11)
#define OSD_MODE_VHRSZ (1 << 10)
#define OSD_MODE_FSINV (1 << 9)
#define OSD_MODE_BCLUT (1 << 8)
#define OSD_MODE_CABG (0xff << 0)
#define OSD_MODE_CABG_SHIFT 0
#define OSD_VIDWINMD_VFINV (1 << 15)
#define OSD_VIDWINMD_V1EFC (1 << 14)
#define OSD_VIDWINMD_VHZ1 (3 << 12)
#define OSD_VIDWINMD_VHZ1_SHIFT 12
#define OSD_VIDWINMD_VVZ1 (3 << 10)
#define OSD_VIDWINMD_VVZ1_SHIFT 10
#define OSD_VIDWINMD_VFF1 (1 << 9)
#define OSD_VIDWINMD_ACT1 (1 << 8)
#define OSD_VIDWINMD_V0EFC (1 << 6)
#define OSD_VIDWINMD_VHZ0 (3 << 4)
#define OSD_VIDWINMD_VHZ0_SHIFT 4
#define OSD_VIDWINMD_VVZ0 (3 << 2)
#define OSD_VIDWINMD_VVZ0_SHIFT 2
#define OSD_VIDWINMD_VFF0 (1 << 1)
#define OSD_VIDWINMD_ACT0 (1 << 0)
#define OSD_OSDWIN0MD_ATN0E (1 << 14)
#define OSD_OSDWIN0MD_RGB0E (1 << 13)
#define OSD_OSDWIN0MD_CLUTS0 (1 << 12)
#define OSD_OSDWIN0MD_OHZ0 (3 << 10)
#define OSD_OSDWIN0MD_OHZ0_SHIFT 10
#define OSD_OSDWIN0MD_OVZ0 (3 << 8)
#define OSD_OSDWIN0MD_OVZ0_SHIFT 8
#define OSD_OSDWIN0MD_BMW0 (3 << 6)
#define OSD_OSDWIN0MD_BMW0_SHIFT 6
#define OSD_OSDWIN0MD_BLND0 (3 << 3)
#define OSD_OSDWIN0MD_BLND0_SHIFT 3
#define OSD_OSDWIN0MD_TE0 (1 << 2)
#define OSD_OSDWIN0MD_OFF0 (1 << 1)
#define OSD_OSDWIN0MD_OACT0 (1 << 0)
#define OSD_OSDWIN1MD_OASW (1 << 15)
#define OSD_OSDWIN1MD_ATN1E (1 << 14)
#define OSD_OSDWIN1MD_RGB1E (1 << 13)
#define OSD_OSDWIN1MD_CLUTS1 (1 << 12)
#define OSD_OSDWIN1MD_OHZ1 (3 << 10)
#define OSD_OSDWIN1MD_OHZ1_SHIFT 10
#define OSD_OSDWIN1MD_OVZ1 (3 << 8)
#define OSD_OSDWIN1MD_OVZ1_SHIFT 8
#define OSD_OSDWIN1MD_BMW1 (3 << 6)
#define OSD_OSDWIN1MD_BMW1_SHIFT 6
#define OSD_OSDWIN1MD_BLND1 (3 << 3)
#define OSD_OSDWIN1MD_BLND1_SHIFT 3
#define OSD_OSDWIN1MD_TE1 (1 << 2)
#define OSD_OSDWIN1MD_OFF1 (1 << 1)
#define OSD_OSDWIN1MD_OACT1 (1 << 0)
#define OSD_OSDATRMD_OASW (1 << 15)
#define OSD_OSDATRMD_OHZA (3 << 10)
#define OSD_OSDATRMD_OHZA_SHIFT 10
#define OSD_OSDATRMD_OVZA (3 << 8)
#define OSD_OSDATRMD_OVZA_SHIFT 8
#define OSD_OSDATRMD_BLNKINT (3 << 6)
#define OSD_OSDATRMD_BLNKINT_SHIFT 6
#define OSD_OSDATRMD_OFFA (1 << 1)
#define OSD_OSDATRMD_BLNK (1 << 0)
#define OSD_RECTCUR_RCAD (0xff << 8)
#define OSD_RECTCUR_RCAD_SHIFT 8
#define OSD_RECTCUR_CLUTSR (1 << 7)
#define OSD_RECTCUR_RCHW (3 << 4)
#define OSD_RECTCUR_RCHW_SHIFT 4
#define OSD_RECTCUR_RCVW (3 << 1)
#define OSD_RECTCUR_RCVW_SHIFT 1
#define OSD_RECTCUR_RCACT (1 << 0)
#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0)
#define OSD_VIDWIN0OFST_V0LO_SHIFT 0
#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0)
#define OSD_VIDWIN1OFST_V1LO_SHIFT 0
#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0)
#define OSD_OSDWIN0OFST_O0LO_SHIFT 0
#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0)
#define OSD_OSDWIN1OFST_O1LO_SHIFT 0
#define OSD_BASEPX_BPX (0x3ff << 0)
#define OSD_BASEPX_BPX_SHIFT 0
#define OSD_BASEPY_BPY (0x1ff << 0)
#define OSD_BASEPY_BPY_SHIFT 0
#define OSD_VIDWIN0XP_V0X (0x3ff << 0)
#define OSD_VIDWIN0XP_V0X_SHIFT 0
#define OSD_VIDWIN0YP_V0Y (0x1ff << 0)
#define OSD_VIDWIN0YP_V0Y_SHIFT 0
#define OSD_VIDWIN0XL_V0W (0xfff << 0)
#define OSD_VIDWIN0XL_V0W_SHIFT 0
#define OSD_VIDWIN0YL_V0H (0x7ff << 0)
#define OSD_VIDWIN0YL_V0H_SHIFT 0
#define OSD_VIDWIN1XP_V1X (0x3ff << 0)
#define OSD_VIDWIN1XP_V1X_SHIFT 0
#define OSD_VIDWIN1YP_V1Y (0x1ff << 0)
#define OSD_VIDWIN1YP_V1Y_SHIFT 0
#define OSD_VIDWIN1XL_V1W (0xfff << 0)
#define OSD_VIDWIN1XL_V1W_SHIFT 0
#define OSD_VIDWIN1YL_V1H (0x7ff << 0)
#define OSD_VIDWIN1YL_V1H_SHIFT 0
#define OSD_OSDWIN0XP_W0X (0x3ff << 0)
#define OSD_OSDWIN0XP_W0X_SHIFT 0
#define OSD_OSDWIN0YP_W0Y (0x1ff << 0)
#define OSD_OSDWIN0YP_W0Y_SHIFT 0
#define OSD_OSDWIN0XL_W0W (0xfff << 0)
#define OSD_OSDWIN0XL_W0W_SHIFT 0
#define OSD_OSDWIN0YL_W0H (0x7ff << 0)
#define OSD_OSDWIN0YL_W0H_SHIFT 0
#define OSD_OSDWIN1XP_W1X (0x3ff << 0)
#define OSD_OSDWIN1XP_W1X_SHIFT 0
#define OSD_OSDWIN1YP_W1Y (0x1ff << 0)
#define OSD_OSDWIN1YP_W1Y_SHIFT 0
#define OSD_OSDWIN1XL_W1W (0xfff << 0)
#define OSD_OSDWIN1XL_W1W_SHIFT 0
#define OSD_OSDWIN1YL_W1H (0x7ff << 0)
#define OSD_OSDWIN1YL_W1H_SHIFT 0
#define OSD_CURXP_RCSX (0x3ff << 0)
#define OSD_CURXP_RCSX_SHIFT 0
#define OSD_CURYP_RCSY (0x1ff << 0)
#define OSD_CURYP_RCSY_SHIFT 0
#define OSD_CURYL_RCSH (0x7ff << 0)
#define OSD_CURYL_RCSH_SHIFT 0
#define OSD_W0BMP01_PAL01 (0xff << 8)
#define OSD_W0BMP01_PAL01_SHIFT 8
#define OSD_W0BMP01_PAL00 (0xff << 0)
#define OSD_W0BMP01_PAL00_SHIFT 0
#define OSD_W0BMP23_PAL03 (0xff << 8)
#define OSD_W0BMP23_PAL03_SHIFT 8
#define OSD_W0BMP23_PAL02 (0xff << 0)
#define OSD_W0BMP23_PAL02_SHIFT 0
#define OSD_W0BMP45_PAL05 (0xff << 8)
#define OSD_W0BMP45_PAL05_SHIFT 8
#define OSD_W0BMP45_PAL04 (0xff << 0)
#define OSD_W0BMP45_PAL04_SHIFT 0
#define OSD_W0BMP67_PAL07 (0xff << 8)
#define OSD_W0BMP67_PAL07_SHIFT 8
#define OSD_W0BMP67_PAL06 (0xff << 0)
#define OSD_W0BMP67_PAL06_SHIFT 0
#define OSD_W0BMP89_PAL09 (0xff << 8)
#define OSD_W0BMP89_PAL09_SHIFT 8
#define OSD_W0BMP89_PAL08 (0xff << 0)
#define OSD_W0BMP89_PAL08_SHIFT 0
#define OSD_W0BMPAB_PAL11 (0xff << 8)
#define OSD_W0BMPAB_PAL11_SHIFT 8
#define OSD_W0BMPAB_PAL10 (0xff << 0)
#define OSD_W0BMPAB_PAL10_SHIFT 0
#define OSD_W0BMPCD_PAL13 (0xff << 8)
#define OSD_W0BMPCD_PAL13_SHIFT 8
#define OSD_W0BMPCD_PAL12 (0xff << 0)
#define OSD_W0BMPCD_PAL12_SHIFT 0
#define OSD_W0BMPEF_PAL15 (0xff << 8)
#define OSD_W0BMPEF_PAL15_SHIFT 8
#define OSD_W0BMPEF_PAL14 (0xff << 0)
#define OSD_W0BMPEF_PAL14_SHIFT 0
#define OSD_W1BMP0_PAL01 (0xff << 8)
#define OSD_W1BMP0_PAL01_SHIFT 8
#define OSD_W1BMP0_PAL00 (0xff << 0)
#define OSD_W1BMP0_PAL00_SHIFT 0
#define OSD_W1BMP2_PAL03 (0xff << 8)
#define OSD_W1BMP2_PAL03_SHIFT 8
#define OSD_W1BMP2_PAL02 (0xff << 0)
#define OSD_W1BMP2_PAL02_SHIFT 0
#define OSD_W1BMP4_PAL05 (0xff << 8)
#define OSD_W1BMP4_PAL05_SHIFT 8
#define OSD_W1BMP4_PAL04 (0xff << 0)
#define OSD_W1BMP4_PAL04_SHIFT 0
#define OSD_W1BMP6_PAL07 (0xff << 8)
#define OSD_W1BMP6_PAL07_SHIFT 8
#define OSD_W1BMP6_PAL06 (0xff << 0)
#define OSD_W1BMP6_PAL06_SHIFT 0
#define OSD_W1BMP8_PAL09 (0xff << 8)
#define OSD_W1BMP8_PAL09_SHIFT 8
#define OSD_W1BMP8_PAL08 (0xff << 0)
#define OSD_W1BMP8_PAL08_SHIFT 0
#define OSD_W1BMPA_PAL11 (0xff << 8)
#define OSD_W1BMPA_PAL11_SHIFT 8
#define OSD_W1BMPA_PAL10 (0xff << 0)
#define OSD_W1BMPA_PAL10_SHIFT 0
#define OSD_W1BMPC_PAL13 (0xff << 8)
#define OSD_W1BMPC_PAL13_SHIFT 8
#define OSD_W1BMPC_PAL12 (0xff << 0)
#define OSD_W1BMPC_PAL12_SHIFT 0
#define OSD_W1BMPE_PAL15 (0xff << 8)
#define OSD_W1BMPE_PAL15_SHIFT 8
#define OSD_W1BMPE_PAL14 (0xff << 0)
#define OSD_W1BMPE_PAL14_SHIFT 0
#define OSD_MISCCT_RGBEN (1 << 7)
#define OSD_MISCCT_RGBWIN (1 << 6)
#define OSD_MISCCT_TMON (1 << 5)
#define OSD_MISCCT_RSEL (1 << 4)
#define OSD_MISCCT_CPBSY (1 << 3)
#define OSD_MISCCT_PPSW (1 << 2)
#define OSD_MISCCT_PPRV (1 << 1)
#define OSD_CLUTRAMY_Y (0xff << 8)
#define OSD_CLUTRAMY_Y_SHIFT 8
#define OSD_CLUTRAMY_CB (0xff << 0)
#define OSD_CLUTRAMY_CB_SHIFT 0
#define OSD_CLUTRAM_CR (0xff << 8)
#define OSD_CLUTRAM_CR_SHIFT 8
#define OSD_CLUTRAM_CADDR (0xff << 0)
#define OSD_CLUTRAM_CADDR_SHIFT 0
#define OSD_TRANSPVA_RGBTRANS (0xff << 0)
#define OSD_TRANSPVA_RGBTRANS_SHIFT 0
#define LCD 0
#define NTSC 1
#define PAL 2
#define COMPOSITE 1
#define SVIDEO 2
#define COMPONENT 3
#define RGB 4
/* define the custom FBIO_WAITFORVSYNC ioctl */
#define FBIO_WAITFORVSYNC _IOW('F', 0x20, u_int32_t)
#define FBIO_SETATTRIBUTE _IOW('F', 0x21, struct fb_fillrect)
#define FBIO_SETPOSX _IOW('F', 0x22, u_int32_t)
#define FBIO_SETPOSY _IOW('F', 0x23, u_int32_t)
struct zoom_params
{
u_int32_t window_id;
u_int32_t zoom_h;
u_int32_t zoom_v;
};
#define FBIO_SETZOOM _IOW('F', 0x24, struct zoom_params)
#define FBIO_GETSTD _IOR('F', 0x25, u_int32_t)
#endif /* _DAVINCIFB_H_ */
......@@ -195,7 +195,7 @@
#define INVERT_VOL(val) (0x7f - val)
/* Default output volume (inverted) */
#define DEFAULT_VOL INVERT_VOL(0x50)
#define DEFAULT_VOL INVERT_VOL(0x60)
/* Default input volume */
#define DEFAULT_GAIN 0x20
......
......@@ -29,7 +29,7 @@
#include <asm/plat-sffsdr/sffsdr-fpga.h>
#endif
#include <mach/mcbsp.h>
#include <mach/asp.h>
#include <mach/edma.h>
#include "../codecs/pcm3008.h"
......@@ -63,8 +63,13 @@ static int sffsdr_hw_params(struct snd_pcm_substream *substream,
}
#endif
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
/* Set cpu DAI configuration:
* CLKX and CLKR are the inputs for the Sample Rate Generator.
* FSX and FSR are outputs, driven by the sample Rate Generator. */
ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_RIGHT_J |
SND_SOC_DAIFMT_CBM_CFS |
SND_SOC_DAIFMT_IB_NF);
if (ret < 0)
return ret;
......@@ -115,15 +120,15 @@ static struct snd_soc_device sffsdr_snd_devdata = {
static struct resource sffsdr_snd_resources[] = {
{
.start = DAVINCI_MCBSP_BASE,
.end = DAVINCI_MCBSP_BASE + SZ_8K - 1,
.start = DAVINCI_ASP0_BASE,
.end = DAVINCI_ASP0_BASE + SZ_8K - 1,
.flags = IORESOURCE_MEM,
},
};
static struct evm_snd_platform_data sffsdr_snd_data = {
.tx_dma_ch = DAVINCI_DMA_MCBSP_TX,
.rx_dma_ch = DAVINCI_DMA_MCBSP_RX,
.tx_dma_ch = DAVINCI_DMA_ASP0_TX,
.rx_dma_ch = DAVINCI_DMA_ASP0_RX,
};
static struct platform_device *sffsdr_snd_device;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment