diff options
| author | Frans Meulenbroeks <fransmeulenbroeks@gmail.com> | 2010-01-06 21:32:39 +0100 |
|---|---|---|
| committer | Frans Meulenbroeks <fransmeulenbroeks@gmail.com> | 2010-01-06 21:32:39 +0100 |
| commit | b2a6f7304b70600e5c9b131c353b81346b82158d (patch) | |
| tree | 4c355cc06586aece4351f2cc586f81e5e79d03ed | |
| parent | c7ed94a12cdcc4b8e98698683022ac1bd328ef0c (diff) | |
linux-kirkwood: moved kernel to 2.6.33-rc1
added patches for video and audio for openrd client
made some configuration changes in defconfig to support those
| -rw-r--r-- | recipes/linux/linux-kirkwood/0001-ARM-Kirkwood-Sound-Sound-driver-added.patch | 3514 | ||||
| -rw-r--r-- | recipes/linux/linux-kirkwood/0001-OpenRD-Client-PCIe-Initialize-PCI-express-and-i2c.patch | 36 | ||||
| -rw-r--r-- | recipes/linux/linux-kirkwood/0003-ARM-Kirkwood-Sound-Sound-driver-added.patch | 208 | ||||
| -rw-r--r-- | recipes/linux/linux-kirkwood/openrd-client/0002-OpenRD-Client-Volari-Z11-driver-added.patch | 29767 | ||||
| -rw-r--r-- | recipes/linux/linux-kirkwood/openrd-client/defconfig | 1292 | ||||
| -rw-r--r-- | recipes/linux/linux-kirkwood_2.6.33-rc1.bb (renamed from recipes/linux/linux-kirkwood_2.6.32-rc4.bb) | 13 |
6 files changed, 33753 insertions, 1077 deletions
diff --git a/recipes/linux/linux-kirkwood/0001-ARM-Kirkwood-Sound-Sound-driver-added.patch b/recipes/linux/linux-kirkwood/0001-ARM-Kirkwood-Sound-Sound-driver-added.patch new file mode 100644 index 0000000000..fc17a089c5 --- /dev/null +++ b/recipes/linux/linux-kirkwood/0001-ARM-Kirkwood-Sound-Sound-driver-added.patch @@ -0,0 +1,3514 @@ +From 89aa6dd15306a1ce11da0f2cb67bda74999e178e Mon Sep 17 00:00:00 2001 +From: Tanmay Upadhyay <tanmay.upadhyay@einfochips.com> +Date: Tue, 24 Nov 2009 21:49:24 +0530 +Subject: [PATCH] ARM: Kirkwood: Sound: Sound driver added + +The driver is based on the Marvell kirkwood sound driver available in +2.6.22.18 kernel. + +Signed-off-by: Tanmay Upadhyay <tanmay.upadhyay@einfochips.com> +--- + arch/arm/mach-kirkwood/common.c | 39 + + arch/arm/mach-kirkwood/common.h | 2 + + arch/arm/mach-kirkwood/include/mach/kirkwood.h | 3 + + arch/arm/mach-kirkwood/openrd_client-setup.c | 27 + + include/linux/mv88fx_audio.h | 111 ++ + sound/soc/Kconfig | 1 + + sound/soc/Makefile | 1 + + sound/soc/kirkwood/Kconfig | 29 + + sound/soc/kirkwood/Makefile | 7 + + sound/soc/kirkwood/cs42l51.c | 304 +++++ + sound/soc/kirkwood/cs42l51.h | 59 + + sound/soc/kirkwood/kirkwood_audio_hal.c | 821 +++++++++++++ + sound/soc/kirkwood/kirkwood_audio_hal.h | 109 ++ + sound/soc/kirkwood/kirkwood_audio_regs.h | 310 +++++ + sound/soc/kirkwood/kirkwood_pcm.c | 1505 ++++++++++++++++++++++++ + 15 files changed, 3328 insertions(+), 0 deletions(-) + create mode 100644 include/linux/mv88fx_audio.h + create mode 100644 sound/soc/kirkwood/Kconfig + create mode 100644 sound/soc/kirkwood/Makefile + create mode 100644 sound/soc/kirkwood/cs42l51.c + create mode 100644 sound/soc/kirkwood/cs42l51.h + create mode 100644 sound/soc/kirkwood/kirkwood_audio_hal.c + create mode 100644 sound/soc/kirkwood/kirkwood_audio_hal.h + create mode 100644 sound/soc/kirkwood/kirkwood_audio_regs.h + create mode 100644 sound/soc/kirkwood/kirkwood_pcm.c + +diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c +index 0acb61f..4d66c06 100644 +--- a/arch/arm/mach-kirkwood/common.c ++++ b/arch/arm/mach-kirkwood/common.c +@@ -15,6 +15,7 @@ + #include <linux/mbus.h> + #include <linux/mv643xx_eth.h> + #include <linux/mv643xx_i2c.h> ++#include <linux/mv88fx_audio.h> + #include <linux/ata_platform.h> + #include <linux/mtd/nand.h> + #include <linux/spi/orion_spi.h> +@@ -969,3 +970,41 @@ static int __init kirkwood_clock_gate(void) + return 0; + } + late_initcall(kirkwood_clock_gate); ++ ++/***************************************************************************** ++ * Audio ++ ****************************************************************************/ ++ ++static struct resource kirkwood_audio_resources[] = { ++ [0] = { ++ .start = AUDIO_PHYS_BASE, ++ .end = AUDIO_PHYS_BASE + SZ_16K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_KIRKWOOD_I2S, ++ .end = IRQ_KIRKWOOD_I2S, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 kirkwood_audio_dmamask = 0xFFFFFFFFUL; ++ ++static struct platform_device kirkwood_audio = { ++ .name = MV88FX_AUDIO_NAME, ++ .id = -1, ++ .num_resources = ARRAY_SIZE(kirkwood_audio_resources), ++ .resource = kirkwood_audio_resources, ++ .dev = { ++ .dma_mask = &kirkwood_audio_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++}; ++ ++void __init kirkwood_audio_init(struct mv88fx_snd_platform_data *audio_data) ++{ ++ kirkwood_clk_ctrl |= CGC_AUDIO; ++ kirkwood_audio.dev.platform_data = audio_data; ++ ++ platform_device_register(&kirkwood_audio); ++} +diff --git a/arch/arm/mach-kirkwood/common.h b/arch/arm/mach-kirkwood/common.h +index d7de434..b79a25c 100644 +--- a/arch/arm/mach-kirkwood/common.h ++++ b/arch/arm/mach-kirkwood/common.h +@@ -16,6 +16,7 @@ struct mv643xx_eth_platform_data; + struct mv_sata_platform_data; + struct mvsdio_platform_data; + struct mtd_partition; ++struct mv88fx_snd_platform_data; + + /* + * Basic Kirkwood init functions used early by machine-setup. +@@ -41,6 +42,7 @@ void kirkwood_i2c_init(void); + void kirkwood_uart0_init(void); + void kirkwood_uart1_init(void); + void kirkwood_nand_init(struct mtd_partition *parts, int nr_parts, int delay); ++void kirkwood_audio_init(struct mv88fx_snd_platform_data *audio_data); + + extern int kirkwood_tclk; + extern struct sys_timer kirkwood_timer; +diff --git a/arch/arm/mach-kirkwood/include/mach/kirkwood.h b/arch/arm/mach-kirkwood/include/mach/kirkwood.h +index 54c1327..90ced65 100644 +--- a/arch/arm/mach-kirkwood/include/mach/kirkwood.h ++++ b/arch/arm/mach-kirkwood/include/mach/kirkwood.h +@@ -95,6 +95,9 @@ + + #define SDIO_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0x90000) + ++#define AUDIO_PHYS_BASE (KIRKWOOD_REGS_PHYS_BASE | 0xA0000) ++#define AUDIO_VIRT_BASE (KIRKWOOD_REGS_VIRT_BASE | 0xA0000) ++ + /* + * Supported devices and revisions. + */ +diff --git a/arch/arm/mach-kirkwood/openrd_client-setup.c b/arch/arm/mach-kirkwood/openrd_client-setup.c +index a55a1bc..72acc22 100644 +--- a/arch/arm/mach-kirkwood/openrd_client-setup.c ++++ b/arch/arm/mach-kirkwood/openrd_client-setup.c +@@ -14,11 +14,13 @@ + #include <linux/mtd/partitions.h> + #include <linux/ata_platform.h> + #include <linux/mv643xx_eth.h> ++#include <linux/mv88fx_audio.h> + #include <linux/gpio.h> + #include <asm/mach-types.h> + #include <asm/mach/arch.h> + #include <mach/kirkwood.h> + #include <plat/mvsdio.h> ++#include <linux/autoconf.h> + #include "common.h" + #include "mpp.h" + +@@ -59,6 +61,21 @@ static unsigned int openrd_client_mpp_config[] __initdata = { + 0 + }; + ++static struct mv88fx_snd_platform_data openrd_client_audio_data = { ++ .i2c_bus_no = 0, ++ .i2c_address = 0x4A, ++/* 0 - NA, 1 - mono, 2 - stereo */ ++#ifdef CONFIG_SND_MV88FX_SOC_I2S ++ .i2s_rec = 1, ++ .i2s_play = 2, ++#else ++ .spdif_rec = 1, ++ .spdif_play = 2, ++#endif ++ .dram = &kirkwood_mbus_dram_info, ++ .base_offset = AUDIO_PHYS_BASE - KIRKWOOD_REGS_PHYS_BASE, ++}; ++ + static void __init openrd_client_init(void) + { + /* +@@ -78,6 +95,16 @@ static void __init openrd_client_init(void) + + kirkwood_sata_init(&openrd_client_sata_data); + kirkwood_sdio_init(&openrd_client_mvsdio_data); ++ ++ /* initialize i2c */ ++ kirkwood_i2c_init(); ++ ++#if defined(CONFIG_SND_MV88FX_SOC) || defined(CONFIG_SND_MV88FX_SOC_MODULE) ++ /* If built as a part of kernel or as a module ++ * initialize audio */ ++ openrd_client_audio_data.tclk = kirkwood_tclk, ++ kirkwood_audio_init(&openrd_client_audio_data); ++#endif + } + + MACHINE_START(OPENRD_CLIENT, "Marvell OpenRD Client Board") +diff --git a/include/linux/mv88fx_audio.h b/include/linux/mv88fx_audio.h +new file mode 100644 +index 0000000..6d36a3f +--- /dev/null ++++ b/include/linux/mv88fx_audio.h +@@ -0,0 +1,111 @@ ++/* ++ * ++ * Marvell Orion Alsa Sound driver ++ * ++ * Author: Maen Suleiman ++ * Copyright (C) 2008 Marvell Ltd. ++ * ++ * ++ * This program 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. ++ * ++ */ ++ ++#ifndef __LINUX_MV88FX_SND_H ++#define __LINUX_MV88FX_SND_H ++ ++#include <linux/mbus.h> ++ ++#define MV88FX_AUDIO_NAME "mv88fx_snd" ++ ++#undef MV88FX_SND_DEBUG ++#ifdef MV88FX_SND_DEBUG ++#define mv88fx_snd_debug(fmt, arg...) printk(KERN_DEBUG fmt, ##arg) ++#else ++ #define mv88fx_snd_debug(a...) ++#endif ++ ++#define MV_AUDIO_MAX_ADDR_DECODE_WIN 2 ++#define MV_AUDIO_RECORD_WIN_NUM 0 ++#define MV_AUDIO_PLAYBACK_WIN_NUM 1 ++ ++#define MV_AUDIO_WIN_CTRL_REG(win) (0xA04 + ((win)<<3)) ++#define MV_AUDIO_WIN_BASE_REG(win) (0xA00 + ((win)<<3)) ++ ++struct mv88fx_snd_platform_data { ++ u8 i2c_bus_no; ++ u16 i2c_address; ++ u32 spdif_rec; ++ u32 spdif_play; ++ u32 i2s_rec; ++ u32 i2s_play; ++ u32 tclk; ++ u32 base_offset; ++ struct mbus_dram_target_info *dram; ++}; ++ ++struct mv88fx_snd_stream { ++ struct snd_pcm_substream *substream; ++ struct device *dev; ++ int direction; /* playback or capture */ ++ #define PLAYBACK 0 ++ #define CAPTURE 1 ++ unsigned int dig_mode; /* i2s,spdif,both */ ++ #define I2S 1 ++ #define SPDIF 2 ++ int stereo; /* mono, stereo */ ++ int mono_mode; /* both mono, left mono, right mono */ ++ #define MONO_BOTH 0 ++ #define MONO_LEFT 1 ++ #define MONO_RIGHT 2 ++ int clock_src; ++ #define DCO_CLOCK 0 ++ #define SPCR_CLOCK 1 ++ #define EXTERN_CLOCK 2 ++ int rate; ++ int stat_mem; /* Channel status source*/ ++ int format; ++ #define SAMPLE_32IN32 0 ++ #define SAMPLE_24IN32 1 ++ #define SAMPLE_20IN32 2 ++ #define SAMPLE_16IN32 3 ++ #define SAMPLE_16IN16 4 ++ unsigned int dma_addr; ++ unsigned int dma_size; ++ unsigned int period_size; ++ unsigned int spdif_status[4]; /* SPDIF status */ ++ unsigned char *area; /* virtual pointer */ ++ dma_addr_t addr; /* physical address */ ++}; ++ ++struct mv88fx_snd_chip { ++ struct mv88fx_snd_stream *stream[2]; /* run time values*/ ++ struct mv88fx_snd_stream *stream_defaults[2]; /* default values*/ ++ spinlock_t reg_lock; /* Register access spinlock */ ++ struct resource *res; /* resource for IRQ and base*/ ++ void __iomem *base; /* Audio base address of the host */ ++ unsigned int audio_offset; /* Offset to audio base register ++ * from internal base register */ ++ int irq; ++ int loopback; /* When Loopback is enabled, playback ++ * data is looped back to be recorded */ ++ int ch_stat_valid; /* Playback SPDIF channel validity bit ++ * value when REG selected */ ++ int burst; /* DMA Burst Size */ ++ ++ #define SPDIF_MEM_STAT 0 ++ #define SPDIF_REG_STAT 1 ++ unsigned int dco_ctrl_offst; ++ int pcm_mode; /* pcm, nonpcm*/ ++ #define PCM 0 ++ #define NON_PCM 1 ++ int stereo; ++}; ++ ++#define MV88FX_SND_MIN_PERIODS 8 ++#define MV88FX_SND_MAX_PERIODS 16 ++#define MV88FX_SND_MIN_PERIOD_BYTES 0x4000 ++#define MV88FX_SND_MAX_PERIOD_BYTES 0x4000 ++ ++#endif /* __LINUX_MV88FX_SND_H */ +diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig +index b1749bc..9fc88d8 100644 +--- a/sound/soc/Kconfig ++++ b/sound/soc/Kconfig +@@ -36,6 +36,7 @@ source "sound/soc/s3c24xx/Kconfig" + source "sound/soc/s6000/Kconfig" + source "sound/soc/sh/Kconfig" + source "sound/soc/txx9/Kconfig" ++source "sound/soc/kirkwood/Kconfig" + + # Supported codecs + source "sound/soc/codecs/Kconfig" +diff --git a/sound/soc/Makefile b/sound/soc/Makefile +index 0c5eac0..664850d 100644 +--- a/sound/soc/Makefile ++++ b/sound/soc/Makefile +@@ -14,3 +14,4 @@ obj-$(CONFIG_SND_SOC) += s3c24xx/ + obj-$(CONFIG_SND_SOC) += s6000/ + obj-$(CONFIG_SND_SOC) += sh/ + obj-$(CONFIG_SND_SOC) += txx9/ ++obj-$(CONFIG_SND_SOC) += kirkwood/ +diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig +new file mode 100644 +index 0000000..d6a7e2f +--- /dev/null ++++ b/sound/soc/kirkwood/Kconfig +@@ -0,0 +1,29 @@ ++config SND_MV88FX_SOC ++ tristate "SoC Audio for the Marvell 88FX chip" ++ depends on ARCH_KIRKWOOD ++ help ++ Say Y or M if you want to add support for codecs attached to ++ the MV88FX I2S or SPD interface. You will also need ++ to select the audio interfaces to support below. ++ ++choice ++ prompt "Audio Interface" ++ default SND_MV88FX_SOC_I2S ++ depends on SND_MV88FX_SOC ++ ++config SND_MV88FX_SOC_I2S ++ bool "I2S" ++ ++config SND_MV88FX_SOC_SPDIF ++ bool "SPDIF" ++ ++endchoice ++ ++choice ++ prompt "Codec IC" ++ default SND_SOC_CS42L51 ++ depends on SND_MV88FX_SOC ++ ++config SND_SOC_CS42L51 ++ bool "CS42L51" ++endchoice +diff --git a/sound/soc/kirkwood/Makefile b/sound/soc/kirkwood/Makefile +new file mode 100644 +index 0000000..57674ad +--- /dev/null ++++ b/sound/soc/kirkwood/Makefile +@@ -0,0 +1,7 @@ ++ ++snd-soc-kirkwood-objs := kirkwood_pcm.o kirkwood_audio_hal.o ++ifdef CONFIG_SND_SOC_CS42L51 ++snd-soc-kirkwood-objs += cs42l51.o ++endif ++ ++obj-$(CONFIG_SND_MV88FX_SOC) += snd-soc-kirkwood.o +diff --git a/sound/soc/kirkwood/cs42l51.c b/sound/soc/kirkwood/cs42l51.c +new file mode 100644 +index 0000000..f5a22f9 +--- /dev/null ++++ b/sound/soc/kirkwood/cs42l51.c +@@ -0,0 +1,304 @@ ++/* ++ * ++ * Marvell Orion Alsa Sound driver ++ * ++ * Author: Maen Suleiman ++ * Copyright (C) 2008 Marvell Ltd. ++ * ++ * ++ * This program 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. ++ * ++ */ ++#include <linux/ioport.h> ++#include <linux/platform_device.h> ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/version.h> ++#include <linux/i2c.h> ++#include <sound/core.h> ++#include <sound/initval.h> ++#include <sound/control.h> ++#include <sound/pcm.h> ++#include <sound/asoundef.h> ++#include <sound/asound.h> ++ ++#include "cs42l51.h" ++ ++/* FIXME: This code is not written in driver module style. This is written as ++ * helper for SOC driver */ ++ ++struct i2c_client *client; ++ ++static int cs42l51_add_i2c_device(unsigned char i2c_bus_no, ++ unsigned short i2c_add) ++{ ++ struct i2c_board_info info; ++ struct i2c_adapter *adapter; ++ ++ memset(&info, 0, sizeof(struct i2c_board_info)); ++ info.addr = i2c_add; ++ strlcpy(info.type, "cs42l51", I2C_NAME_SIZE); ++ ++ adapter = i2c_get_adapter(i2c_bus_no); ++ if (!adapter) { ++ snd_printk("can't get i2c adapter\n"); ++ return -ENODEV; ++ } ++ ++ client = i2c_new_device(adapter, &info); ++ i2c_put_adapter(adapter); ++ if (!client) { ++ snd_printk("can't add i2c device\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++void cs42l51_del_i2c_device(void) ++{ ++ if (client) ++ i2c_unregister_device(client); ++ client = NULL; ++} ++ ++/* ++ * offset: Register offset to start reading with ++ * buf : Pointer to the buffer to store the read data ++ * num : Number of registers to read ++ * ++ * Returns -ve errorno else number of registers read ++ */ ++ ++int cs42l51_reg_read(unsigned char offset, unsigned char *buf, int num) ++{ ++ int ret = 0; ++ ++ /* Set autoincrement bit */ ++ offset |= CODEC_INCR_ADDR; ++ ++ /* Send register offset */ ++ ret = i2c_master_send(client, &offset, 1); ++ if (ret != 1) { ++ snd_printd("Could not write register offset\n"); ++ return 0; ++ } ++ ++ return i2c_master_recv(client, buf, num); ++} ++ ++/* ++ * offset: Register offset to write ++ * data : Data to be written ++ * ++ * Returns -ve errorno else number of registers written (=1) ++ */ ++ ++int cs42l51_reg_write(unsigned char offset, unsigned char data) ++{ ++ int ret = 0; ++ unsigned char buf[2]; ++ ++ buf[0] = offset; ++ buf[1] = data; ++ ++ /* Send register offset & data */ ++ ret = i2c_master_send(client, &buf[0], 2); ++ ++ return (ret == 2) ? 1 : ret; ++} ++ ++int codec_init(int adc_mode, int digital_if_format, ++ unsigned char i2c_bus_no, unsigned short i2c_add) ++{ ++ unsigned char reg_data; ++ ++ if (cs42l51_add_i2c_device(i2c_bus_no, i2c_add)) ++ return 1; ++ ++ if (cs42l51_reg_read(CODEC_ID_REG, ®_data, 1) < 0) ++ goto codec_init_error; ++ ++ if (CODEC_CHIP_ID != (reg_data >> 3) || ++ CODEC_REV_ID != (reg_data & 0x7)) { ++ snd_printd("Error: Invalid Cirrus Logic chip/rev ID!\n"); ++ return 1; ++ } ++ ++ if (cs42l51_reg_read(CODEC_IF_CTRL_REG, ®_data, 1) < 0) ++ goto codec_init_error; ++ ++ reg_data = (reg_data & ~(0x7<<3)) | (digital_if_format << 3); ++ ++ if (LEFT_JUSTIFIED_MODE == adc_mode) ++ reg_data &= (~0x4); ++ else ++ reg_data |= 0x4; ++ ++ if (cs42l51_reg_write(CODEC_IF_CTRL_REG, reg_data) < 0) ++ goto codec_init_error; ++ ++ return 0; ++ ++codec_init_error: ++ snd_printd("I2C error\n"); ++ return 1; ++} ++ ++/* ++ * Initialize the audio decoder. ++ */ ++ ++int cs42l51_init(int adc_mode, int digital_if_format, int rec, ++ unsigned char i2c_bus_no, unsigned short i2c_add) ++{ ++ if (codec_init(adc_mode, digital_if_format, i2c_bus_no, i2c_add)) { ++ snd_printk("Error: Audio Codec init failed\n"); ++ return 1; ++ } ++ ++ /* Use the signal processor */ ++ if (cs42l51_reg_write(0x9, 0x40) < 0) ++ goto error; ++ ++ /* Unmute PCM-A & PCM-B and set default */ ++ if (cs42l51_reg_write(0x10, 0x60) < 0) ++ goto error; ++ if (cs42l51_reg_write(0x11, 0x60) < 0) ++ goto error; ++ ++ /* default for AOUTx */ ++ if (cs42l51_reg_write(0x16, 0x05) < 0) ++ goto error; ++ if (cs42l51_reg_write(0x17, 0x05) < 0) ++ goto error; ++ ++ /* swap channels */ ++ if (cs42l51_reg_write(0x18, 0xff) < 0) ++ goto error; ++ ++ /* MIC Power Control: power down mIC in channel B, power on channel A ++ * Recommended seq. in datasheet: ++ * 1. Enable the PDN bit ++ * 2. Enable power-down for the selected channels ++ * 3. Disable the PDN bit */ ++ ++ /* Note: Tested for mono recording only */ ++ if (!rec) { ++ /* Enable power down */ ++ if (cs42l51_reg_write(0x2, 0x11) < 0) ++ goto error; ++ ++ /* No record - Power down both channels */ ++ if (cs42l51_reg_write(0x2, 0x17) < 0) ++ goto error; ++ ++ /* Disable power down */ ++ if (cs42l51_reg_write(0x2, 0x16) < 0) ++ goto error; ++ } else { ++ if (rec == 2) { ++ /* Setreo recording - by default both channels are up */ ++ ++ /* MIC In channel selection - Select channel 3 ++ * unmute both channels */ ++ if (cs42l51_reg_write(0x7, 0xF0) < 0) ++ goto error; ++ ++ /* Power up mic pre-amplifier for both channels */ ++ if (cs42l51_reg_write(0x3, 0xA0) < 0) ++ goto error; ++ } else { ++ /* Enable power down */ ++ if (cs42l51_reg_write(0x2, 0x11) < 0) ++ goto error; ++ ++ /* Mono recording - Power down Channel B */ ++ if (cs42l51_reg_write(0x2, 0x15) < 0) ++ goto error; ++ ++ /* Disable power down */ ++ if (cs42l51_reg_write(0x2, 0x14) < 0) ++ goto error; ++ ++ /* MIC In channel selection - Select channel 3 ++ * Mute Channel B */ ++ if (cs42l51_reg_write(0x7, 0xF2) < 0) ++ goto error; ++ ++ /* Power down mic pre-amplifier for Channel B*/ ++ if (cs42l51_reg_write(0x3, 0xA8) < 0) ++ goto error; ++ } ++ } ++ ++ return 0; ++error: ++ snd_printk("I2C error\n"); ++ return 1; ++} ++ ++#define AUD_NUM_VOLUME_STEPS (40) ++static unsigned char auddec_volume_mapping[AUD_NUM_VOLUME_STEPS] = ++{ ++ 0x19, 0xB2, 0xB7, 0xBD, 0xC3, 0xC9, 0xCF, 0xD5, ++ 0xD8, 0xE1, 0xE7, 0xED, 0xF3, 0xF9, 0xFF, 0x00, ++ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ++ 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, ++ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 ++}; ++ ++ ++/* ++ * Get the audio decoder volume for both channels. ++ * 0 is lowest volume, AUD_NUM_VOLUME_STEPS-1 is the highest volume. ++ */ ++ ++void cs42l51_vol_get(unsigned char *vol_list) ++{ ++ unsigned char reg_data[2]; ++ unsigned char i, vol_idx = 0; ++ ++ if (cs42l51_reg_read(0x16 + vol_idx, reg_data, 2) < 0) { ++ snd_printd("I2C error\n"); ++ snd_printk("Couldn't get volume\n"); ++ return; ++ } ++ ++ for (; vol_idx < 2; vol_idx++) { ++ /* Look for the index that mapps to this dB value. */ ++ for (i = 0; i < AUD_NUM_VOLUME_STEPS; i++) { ++ if (reg_data[vol_idx] == auddec_volume_mapping[i]) ++ break; ++ if ((auddec_volume_mapping[i] > ++ auddec_volume_mapping[AUD_NUM_VOLUME_STEPS-1]) ++ && (reg_data[vol_idx] > auddec_volume_mapping[i]) ++ && (reg_data[vol_idx] < auddec_volume_mapping[i+1])) ++ break; ++ } ++ vol_list[vol_idx] = i; ++ } ++} ++ ++/* ++ * Set the audio decoder volume for both channels. ++ * 0 is lowest volume, AUD_NUM_VOLUME_STEPS-1 is the highest volume. ++ */ ++void cs42l51_vol_set(unsigned char *vol_list) ++{ ++ unsigned int vol_idx; ++ ++ for (vol_idx = 0; vol_idx < 2; vol_idx++) { ++ if (vol_list[vol_idx] >= AUD_NUM_VOLUME_STEPS) ++ vol_list[vol_idx] = AUD_NUM_VOLUME_STEPS - 1; ++ ++ if (cs42l51_reg_write(0x16 + vol_idx, ++ auddec_volume_mapping[vol_list[vol_idx]]) < 0) { ++ snd_printd("I2C error\n"); ++ snd_printk("Couldn't set volume\n"); ++ return; ++ } ++ } ++} +diff --git a/sound/soc/kirkwood/cs42l51.h b/sound/soc/kirkwood/cs42l51.h +new file mode 100644 +index 0000000..f4e7951 +--- /dev/null ++++ b/sound/soc/kirkwood/cs42l51.h +@@ -0,0 +1,59 @@ ++/* ++ * Audio codec CS42L51 data definition file ++ */ ++ ++#ifndef _CS42L51_H_ ++#define _CS42L51_H_ ++ ++#define CODEC_CHIP_ID 0x1B ++#define CODEC_REV_ID 0x1 ++ ++#define CODEC_ID_REG 0x1 ++#define CODEC_IF_CTRL_REG 0x4 ++#define CODEC_ADC_INPUT_INV_MUTE_REG 0x7 ++#define CODEC_DAC_OUTPUT_CTRL_REG 0x8 ++#define CODEC_DAC_CTRL_REG 0x9 ++#define CODEC_PGAA_VOL_CTRL_REG 0xa ++#define CODEC_TONE_CTRL_REG 0x15 ++#define CODEC_VOL_OUTA_CTRL_REG 0x16 ++ ++/* Set bit # 7 to 1 to get into auto incremental addressing mode */ ++#define CODEC_INCR_ADDR 0x80 ++ ++#define FALSE 0 ++#define TRUE 1 ++ ++/* Selects the digital interface format used for the data in on SDIN. */ ++enum dac_digital_if_format { ++ L_JUSTIFIED_UP_TO_24_BIT, ++ I2S_UP_TO_24_BIT, ++ R_JUSTIFIED_UP_TO_24_BIT, ++ R_JUSTIFIED_20_BIT, ++ R_JUSTIFIED_18_BIT, ++ R_JUSTIFIED_16_BIT ++ ++}; ++ ++/* Selects either the I2S or Left-Justified digital interface format for the ++ data on SDOUT. */ ++enum adc_mode { ++ LEFT_JUSTIFIED_MODE, ++ I2S_MODE ++}; ++ ++/* Initialize the Cirrus Logic device */ ++int cs42l51_init(int adc_mode, int digital_if_format, int rec, ++ unsigned char i2c_bus_no, unsigned short i2c_add); ++ ++/* Function to control output volume (playback) */ ++void cs42l51_vol_get(unsigned char *vol_list); ++void cs42l51_vol_set(unsigned char *vol_list); ++ ++/* Function to access the Cirrus Logic CODEC registers */ ++int cs42l51_reg_read(unsigned char offset, unsigned char *buf, int num); ++int cs42l51_reg_write(unsigned char offset, unsigned char data); ++ ++ ++void cs42l51_del_i2c_device(void); ++#endif /* _CS42L51_H_ */ ++ +diff --git a/sound/soc/kirkwood/kirkwood_audio_hal.c b/sound/soc/kirkwood/kirkwood_audio_hal.c +new file mode 100644 +index 0000000..28305e3 +--- /dev/null ++++ b/sound/soc/kirkwood/kirkwood_audio_hal.c +@@ -0,0 +1,821 @@ ++/* ++ * Sound driver for Marvell Kirkwood family SOCs ++ * ++ * 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/io.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <linux/spinlock.h> ++#include <linux/mv88fx_audio.h> ++#include "kirkwood_audio_hal.h" ++ ++static void mv_audio_init(void __iomem *base); ++static void audio_setup_wins(void __iomem *base, ++ struct mbus_dram_target_info *dram); ++static int set_window_as_per_baseadd(void __iomem *base, unsigned int baseadd, ++ unsigned int audio_offset, int win_num); ++ ++/* Clocks Control and Status related*/ ++static int mv_audio_dco_ctrl_set(struct mv_audio_freq_data *dcoCtrl, ++ void __iomem *base); ++ ++/* Audio PlayBack related*/ ++static int mv_audio_playback_control_set(void __iomem *base, unsigned int ++ audio_offset, struct mv_audio_playback_ctrl *ctrl); ++ ++/* Audio SPDIF PlayBack related*/ ++static void mv_spdif_playback_ctrl_set(void __iomem *base, ++ struct mv_spdif_playback_ctrl *ctrl); ++ ++/* Audio I2S PlayBack related*/ ++static int mv_i2s_playback_ctrl_set(void __iomem *base, ++ struct mv_i2s_playback_ctrl *ctrl); ++ ++/* Audio Recording*/ ++static int mv_audio_record_control_set(struct mv_audio_record_ctrl *ctrl, ++ unsigned int audio_offset, void __iomem *base); ++ ++/* SPDIF Recording Related*/ ++static int spdif_record_tclock_set(void __iomem *base, unsigned int tclk); ++ ++/* I2S Recording Related*/ ++static int mv_i2s_record_cntrl_set(struct mv_i2s_record_ctrl *ctrl, ++ void __iomem *base); ++ ++static inline int audio_burst_bytes_num_get(int burst) ++{ ++ switch (burst) { ++ case AUDIO_32BYTE_BURST: ++ return 32; ++ case AUDIO_128BYTE_BURST: ++ return 128; ++ default: ++ return 0xffffffff; ++ } ++} ++ ++int mv88fx_snd_hw_init(struct snd_card *card) ++{ ++ void __iomem *base = chip->base; ++ struct mv88fx_snd_platform_data *platform_data = ++ card->dev->platform_data; ++ ++ if (platform_data->i2s_rec || platform_data->i2s_play) ++ if (codec_init(I2S_MODE, I2S_UP_TO_24_BIT, ++ platform_data->i2s_rec, platform_data->i2c_bus_no, ++ platform_data->i2c_address)) { ++ snd_printk("Initializing CS42L51 failed\n"); ++ return 1; ++ } ++ ++ writel(0xffffffff, (base + MV_AUDIO_INT_CAUSE_REG)); ++ writel(0, (base + MV_AUDIO_INT_MASK_REG)); ++ writel(0, (base + MV_AUDIO_SPDIF_REC_INT_CAUSE_MASK_REG)); ++ ++ mv_audio_init(base); ++ ++ audio_setup_wins(base, platform_data->dram); ++ ++ /* Disable all playback/recording */ ++ writel(readl(base + MV_AUDIO_PLAYBACK_CTRL_REG) & ++ (~(APCR_PLAY_I2S_ENABLE_MASK | APCR_PLAY_SPDIF_ENABLE_MASK)), ++ (base + MV_AUDIO_PLAYBACK_CTRL_REG)); ++ ++ writel(readl(base + MV_AUDIO_RECORD_CTRL_REG) & ++ (~(ARCR_RECORD_SPDIF_EN_MASK | ARCR_RECORD_I2S_EN_MASK)), ++ (base + MV_AUDIO_RECORD_CTRL_REG)); ++ ++ if (spdif_record_tclock_set(base, platform_data->tclk)) { ++ snd_printk("Marvell ALSA driver ERR. SPDIF clock set failed\n"); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++int mv88fx_snd_hw_playback_set(struct mv88fx_snd_chip *chip) ++{ ++ struct mv88fx_snd_stream *audio_stream = chip->stream[PLAYBACK]; ++ struct snd_pcm_substream *substream = audio_stream->substream; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ struct mv_audio_playback_ctrl pcm_play_ctrl; ++ struct mv_i2s_playback_ctrl i2s_play_ctrl; ++ struct mv_spdif_playback_ctrl spdif_play_ctrl; ++ struct mv_audio_freq_data dco_ctrl; ++ ++ dco_ctrl.offset = chip->dco_ctrl_offst; ++ ++ switch (audio_stream->rate) { ++ case 44100: ++ dco_ctrl.baseFreq = AUDIO_FREQ_44_1KH; ++ break; ++ case 48000: ++ dco_ctrl.baseFreq = AUDIO_FREQ_48KH; ++ break; ++ case 96000: ++ dco_ctrl.baseFreq = AUDIO_FREQ_96KH; ++ break; ++ default: ++ snd_printk("Requested rate %d is not supported\n", ++ runtime->rate); return -1; ++ } ++ ++ pcm_play_ctrl.burst = (chip->burst == 128) ? AUDIO_128BYTE_BURST : ++ AUDIO_32BYTE_BURST; ++ ++ pcm_play_ctrl.loopBack = chip->loopback; ++ ++ if (audio_stream->stereo) { ++ pcm_play_ctrl.monoMode = AUDIO_PLAY_MONO_OFF; ++ } else { ++ switch (audio_stream->mono_mode) { ++ case MONO_LEFT: ++ pcm_play_ctrl.monoMode = AUDIO_PLAY_LEFT_MONO; ++ break; ++ case MONO_RIGHT: ++ pcm_play_ctrl.monoMode = AUDIO_PLAY_RIGHT_MONO; ++ break; ++ case MONO_BOTH: ++ default: ++ pcm_play_ctrl.monoMode = AUDIO_PLAY_BOTH_MONO; ++ break; ++ } ++ } ++ ++ if (audio_stream->format == SAMPLE_16IN16) { ++ pcm_play_ctrl.sampleSize = SAMPLE_16BIT; ++ i2s_play_ctrl.sampleSize = SAMPLE_16BIT; ++ } else if (audio_stream->format == SAMPLE_24IN32) { ++ pcm_play_ctrl.sampleSize = SAMPLE_24BIT; ++ i2s_play_ctrl.sampleSize = SAMPLE_24BIT; ++ } else if (audio_stream->format == SAMPLE_32IN32) { ++ pcm_play_ctrl.sampleSize = SAMPLE_32BIT; ++ i2s_play_ctrl.sampleSize = SAMPLE_32BIT; ++ } else { ++ snd_printk("Requested format %d is not supported\n", ++ runtime->format); ++ return -1; ++ } ++ ++ /* buffer and period sizes in frame */ ++ pcm_play_ctrl.bufferPhyBase = audio_stream->dma_addr; ++ pcm_play_ctrl.bufferSize = audio_stream->dma_size; ++ pcm_play_ctrl.intByteCount = audio_stream->period_size; ++ ++ /* I2S playback streem stuff */ ++ /*i2s_play_ctrl.sampleSize = pcm_play_ctrl.sampleSize;*/ ++ i2s_play_ctrl.justification = I2S_JUSTIFIED; ++ i2s_play_ctrl.sendLastFrame = 0; ++ ++ spdif_play_ctrl.nonPcm = FALSE; ++ ++ spdif_play_ctrl.validity = chip->ch_stat_valid; ++ ++ if (audio_stream->stat_mem) { ++ spdif_play_ctrl.userBitsFromMemory = TRUE; ++ spdif_play_ctrl.validityFromMemory = TRUE; ++ spdif_play_ctrl.blockStartInternally = FALSE; ++ } else { ++ spdif_play_ctrl.userBitsFromMemory = FALSE; ++ spdif_play_ctrl.validityFromMemory = FALSE; ++ spdif_play_ctrl.blockStartInternally = TRUE; ++ } ++ ++ /* If this is non-PCM sound, mute I2S channel */ ++ spin_lock_irq(&chip->reg_lock); ++ ++ if (!(readl(chip->base + MV_AUDIO_PLAYBACK_CTRL_REG) & ++ (APCR_PLAY_I2S_ENABLE_MASK | APCR_PLAY_SPDIF_ENABLE_MASK))) { ++ ++ if (mv_audio_dco_ctrl_set(&dco_ctrl, chip->base)) { ++ snd_printk("Failed to initialize DCO clock control.\n"); ++ goto error; ++ } ++ } ++ ++ if (audio_stream->clock_src == DCO_CLOCK) ++ while ((readl(chip->base + MV_AUDIO_SPCR_DCO_STATUS_REG) & ++ ASDSR_DCO_LOCK_MASK) == 0) ++ cpu_relax(); ++ else if (audio_stream->clock_src == SPCR_CLOCK) ++ while ((readl(chip->base + MV_AUDIO_SPCR_DCO_STATUS_REG) & ++ ASDSR_SPCR_LOCK_MASK) == 0) ++ cpu_relax(); ++ ++ if (mv_audio_playback_control_set(chip->base, chip->audio_offset, ++ &pcm_play_ctrl)) { ++ snd_printk("Failed to initialize PCM playback control.\n"); ++ goto error; ++ } ++ ++ if (mv_i2s_playback_ctrl_set(chip->base, &i2s_play_ctrl)) { ++ snd_printk("Failed to initialize I2S playback control.\n"); ++ goto error; ++ } ++ ++ mv_spdif_playback_ctrl_set(chip->base, &spdif_play_ctrl); ++ ++ spin_unlock_irq(&chip->reg_lock); ++ ++ return 0; ++error: ++ spin_unlock_irq(&chip->reg_lock); ++ return -1; ++} ++ ++int mv88fx_snd_hw_capture_set(struct mv88fx_snd_chip *chip) ++{ ++ struct mv88fx_snd_stream *audio_stream = chip->stream[CAPTURE]; ++ struct snd_pcm_substream *substream = audio_stream->substream; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ ++ struct mv_audio_record_ctrl pcm_rec_ctrl; ++ struct mv_i2s_record_ctrl i2s_rec_ctrl; ++ struct mv_audio_freq_data dco_ctrl; ++ ++ dco_ctrl.offset = chip->dco_ctrl_offst; ++ ++ switch (audio_stream->rate) { ++ case 44100: ++ dco_ctrl.baseFreq = AUDIO_FREQ_44_1KH; ++ break; ++ case 48000: ++ dco_ctrl.baseFreq = AUDIO_FREQ_48KH; ++ break; ++ case 96000: ++ dco_ctrl.baseFreq = AUDIO_FREQ_96KH; ++ break; ++ default: ++ snd_printk("Requested rate %d is not supported\n", ++ runtime->rate); return -1; ++ } ++ ++ pcm_rec_ctrl.burst = (chip->burst == 128) ? AUDIO_128BYTE_BURST : ++ AUDIO_32BYTE_BURST; ++ ++ if (audio_stream->format == SAMPLE_16IN16) { ++ pcm_rec_ctrl.sampleSize = SAMPLE_16BIT; ++ } else if (audio_stream->format == SAMPLE_24IN32) { ++ pcm_rec_ctrl.sampleSize = SAMPLE_24BIT; ++ } else if (audio_stream->format == SAMPLE_32IN32) { ++ pcm_rec_ctrl.sampleSize = SAMPLE_32BIT; ++ } else { ++ snd_printk("Requested format %d is not supported\n", ++ runtime->format); ++ return -1; ++ } ++ ++ /* If request for tereo record comes on the boards that doesn't ++ * support stereo recording */ ++ if ((!chip->stereo) && audio_stream->stereo) { ++ snd_printk("Stereo recording is not supported\n"); ++ return -1; ++ } ++ ++ pcm_rec_ctrl.mono = (audio_stream->stereo) ? FALSE : TRUE; ++ ++ if (pcm_rec_ctrl.mono) { ++ switch (audio_stream->mono_mode) { ++ case MONO_LEFT: ++ pcm_rec_ctrl.monoChannel = AUDIO_REC_LEFT_MONO; ++ break; ++ default: ++ case MONO_RIGHT: ++ pcm_rec_ctrl.monoChannel = AUDIO_REC_RIGHT_MONO; ++ break; ++ } ++ ++ } else { ++ pcm_rec_ctrl.monoChannel = AUDIO_REC_LEFT_MONO; ++ } ++ ++ ++ pcm_rec_ctrl.bufferPhyBase = audio_stream->dma_addr; ++ pcm_rec_ctrl.bufferSize = audio_stream->dma_size; ++ ++ pcm_rec_ctrl.intByteCount = audio_stream->period_size; ++ ++ /* I2S record streem stuff */ ++ i2s_rec_ctrl |
