forked from Minki/linux
7b9b5e1170
Move DAS routing setup into the DAS driver itself. This removes the need to duplicate this in each machine driver, of which we'll soon have three. An added advantage is that the machine drivers no longer call the Tegra20- specific DAS functions by name, so the machine driver no longer needs to be split up into Tegra20 and Tegra30 versions. If individual machine drivers need a different routing setup to this default, they can still call the DAS functions to set that up. Long-term, DAS will be a codec driver, and user-space will be able to control its routing, possibly within constraints that the machine driver sets up. Configuring the DAS routing from the DAS driver is a very slight move in that direction. Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
262 lines
5.9 KiB
C
262 lines
5.9 KiB
C
/*
|
|
* tegra_das.c - Tegra DAS driver
|
|
*
|
|
* Author: Stephen Warren <swarren@nvidia.com>
|
|
* Copyright (C) 2010 - NVIDIA, Inc.
|
|
*
|
|
* 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.
|
|
*
|
|
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
#include <mach/iomap.h>
|
|
#include <sound/soc.h>
|
|
#include "tegra_das.h"
|
|
|
|
#define DRV_NAME "tegra-das"
|
|
|
|
static struct tegra_das *das;
|
|
|
|
static inline void tegra_das_write(u32 reg, u32 val)
|
|
{
|
|
__raw_writel(val, das->regs + reg);
|
|
}
|
|
|
|
static inline u32 tegra_das_read(u32 reg)
|
|
{
|
|
return __raw_readl(das->regs + reg);
|
|
}
|
|
|
|
int tegra_das_connect_dap_to_dac(int dap, int dac)
|
|
{
|
|
u32 addr;
|
|
u32 reg;
|
|
|
|
if (!das)
|
|
return -ENODEV;
|
|
|
|
addr = TEGRA_DAS_DAP_CTRL_SEL +
|
|
(dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
|
|
reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
|
|
|
|
tegra_das_write(addr, reg);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac);
|
|
|
|
int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master,
|
|
int sdata1rx, int sdata2rx)
|
|
{
|
|
u32 addr;
|
|
u32 reg;
|
|
|
|
if (!das)
|
|
return -ENODEV;
|
|
|
|
addr = TEGRA_DAS_DAP_CTRL_SEL +
|
|
(dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
|
|
reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
|
|
!!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
|
|
!!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
|
|
!!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
|
|
|
|
tegra_das_write(addr, reg);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap);
|
|
|
|
int tegra_das_connect_dac_to_dap(int dac, int dap)
|
|
{
|
|
u32 addr;
|
|
u32 reg;
|
|
|
|
if (!das)
|
|
return -ENODEV;
|
|
|
|
addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
|
|
(dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
|
|
reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
|
|
dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
|
|
dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
|
|
|
|
tegra_das_write(addr, reg);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap);
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
static int tegra_das_show(struct seq_file *s, void *unused)
|
|
{
|
|
int i;
|
|
u32 addr;
|
|
u32 reg;
|
|
|
|
for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) {
|
|
addr = TEGRA_DAS_DAP_CTRL_SEL +
|
|
(i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
|
|
reg = tegra_das_read(addr);
|
|
seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg);
|
|
}
|
|
|
|
for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) {
|
|
addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
|
|
(i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
|
|
reg = tegra_das_read(addr);
|
|
seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n",
|
|
i, reg);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_das_debug_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, tegra_das_show, inode->i_private);
|
|
}
|
|
|
|
static const struct file_operations tegra_das_debug_fops = {
|
|
.open = tegra_das_debug_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static void tegra_das_debug_add(struct tegra_das *das)
|
|
{
|
|
das->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
|
|
snd_soc_debugfs_root, das,
|
|
&tegra_das_debug_fops);
|
|
}
|
|
|
|
static void tegra_das_debug_remove(struct tegra_das *das)
|
|
{
|
|
if (das->debug)
|
|
debugfs_remove(das->debug);
|
|
}
|
|
#else
|
|
static inline void tegra_das_debug_add(struct tegra_das *das)
|
|
{
|
|
}
|
|
|
|
static inline void tegra_das_debug_remove(struct tegra_das *das)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static int __devinit tegra_das_probe(struct platform_device *pdev)
|
|
{
|
|
struct resource *res, *region;
|
|
int ret = 0;
|
|
|
|
if (das)
|
|
return -ENODEV;
|
|
|
|
das = devm_kzalloc(&pdev->dev, sizeof(struct tegra_das), GFP_KERNEL);
|
|
if (!das) {
|
|
dev_err(&pdev->dev, "Can't allocate tegra_das\n");
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
das->dev = &pdev->dev;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res) {
|
|
dev_err(&pdev->dev, "No memory resource\n");
|
|
ret = -ENODEV;
|
|
goto err;
|
|
}
|
|
|
|
region = devm_request_mem_region(&pdev->dev, res->start,
|
|
resource_size(res), pdev->name);
|
|
if (!region) {
|
|
dev_err(&pdev->dev, "Memory region already claimed\n");
|
|
ret = -EBUSY;
|
|
goto err;
|
|
}
|
|
|
|
das->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
|
if (!das->regs) {
|
|
dev_err(&pdev->dev, "ioremap failed\n");
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1,
|
|
TEGRA_DAS_DAP_SEL_DAC1);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
|
|
goto err;
|
|
}
|
|
ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1,
|
|
TEGRA_DAS_DAC_SEL_DAP1);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
|
|
goto err;
|
|
}
|
|
|
|
tegra_das_debug_add(das);
|
|
|
|
platform_set_drvdata(pdev, das);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
das = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static int __devexit tegra_das_remove(struct platform_device *pdev)
|
|
{
|
|
if (!das)
|
|
return -ENODEV;
|
|
|
|
tegra_das_debug_remove(das);
|
|
|
|
das = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id tegra_das_of_match[] __devinitconst = {
|
|
{ .compatible = "nvidia,tegra20-das", },
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver tegra_das_driver = {
|
|
.probe = tegra_das_probe,
|
|
.remove = __devexit_p(tegra_das_remove),
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = tegra_das_of_match,
|
|
},
|
|
};
|
|
module_platform_driver(tegra_das_driver);
|
|
|
|
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
|
|
MODULE_DESCRIPTION("Tegra DAS driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("platform:" DRV_NAME);
|
|
MODULE_DEVICE_TABLE(of, tegra_das_of_match);
|