281 lines
7.0 KiB
C
281 lines
7.0 KiB
C
|
/* Copyright (c) 2009-2011, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 and
|
||
|
* only 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.
|
||
|
*
|
||
|
*/
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/mutex.h>
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/io.h>
|
||
|
#include <linux/err.h>
|
||
|
#include <linux/gpio.h>
|
||
|
#include <mach/qdsp5v2/aux_pcm.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <mach/debug_mm.h>
|
||
|
|
||
|
/*----------------------------------------------------------------------------
|
||
|
* Preprocessor Definitions and Constants
|
||
|
* -------------------------------------------------------------------------*/
|
||
|
|
||
|
/* define offset of registers here, may put them into platform data */
|
||
|
#define AUX_CODEC_CTL_OFFSET 0x00
|
||
|
#define PCM_PATH_CTL_OFFSET 0x04
|
||
|
#define AUX_CODEC_CTL_OUT_OFFSET 0x08
|
||
|
|
||
|
/* define some bit values in PCM_PATH_CTL register */
|
||
|
#define PCM_PATH_CTL__ADSP_CTL_EN_BMSK 0x8
|
||
|
|
||
|
/* mask and shift */
|
||
|
#define AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK 0x800
|
||
|
#define AUX_CODEC_CTL_PCM_SYNC_LONG_BMSK 0x400
|
||
|
#define AUX_CODEC_CTL_PCM_SYNC_SHORT_BMSK 0x200
|
||
|
#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_SRC_BMSK 0x80
|
||
|
#define AUX_CODEC_CTL_I2S_SAMPLE_CLK_MODE_BMSK 0x40
|
||
|
#define AUX_CODEC_CTL_I2S_RX_MODE_BMSK 0x20
|
||
|
#define AUX_CODEC_CTL_I2S_CLK_MODE_BMSK 0x10
|
||
|
#define AUX_CODEC_CTL_AUX_PCM_MODE_BMSK 0x0b
|
||
|
#define AUX_CODEC_CTL_AUX_CODEC_MODE_BMSK 0x02
|
||
|
|
||
|
/* AUX PCM MODE */
|
||
|
#define MASTER_PRIM_PCM_SHORT 0
|
||
|
#define MASTER_AUX_PCM_LONG 1
|
||
|
#define SLAVE_PRIM_PCM_SHORT 2
|
||
|
|
||
|
struct aux_pcm_state {
|
||
|
void __iomem *aux_pcm_base; /* configure aux pcm through Scorpion */
|
||
|
int dout;
|
||
|
int din;
|
||
|
int syncout;
|
||
|
int clkin_a;
|
||
|
};
|
||
|
|
||
|
static struct aux_pcm_state the_aux_pcm_state;
|
||
|
|
||
|
static void __iomem *get_base_addr(struct aux_pcm_state *aux_pcm)
|
||
|
{
|
||
|
return aux_pcm->aux_pcm_base;
|
||
|
}
|
||
|
|
||
|
/* Set who control aux pcm : adsp or MSM */
|
||
|
void aux_codec_adsp_codec_ctl_en(bool msm_adsp_en)
|
||
|
{
|
||
|
void __iomem *baddr = get_base_addr(&the_aux_pcm_state);
|
||
|
uint32_t val;
|
||
|
|
||
|
if (!IS_ERR(baddr)) {
|
||
|
val = readl(baddr + AUX_CODEC_CTL_OFFSET);
|
||
|
if (msm_adsp_en) { /* adsp */
|
||
|
writel(
|
||
|
((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) |
|
||
|
AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__ADSP_V),
|
||
|
baddr + AUX_CODEC_CTL_OFFSET);
|
||
|
} else { /* MSM */
|
||
|
writel(
|
||
|
((val & ~AUX_CODEC_CTL_ADSP_CODEC_CTL_EN_BMSK) |
|
||
|
AUX_CODEC_CTL__ADSP_CODEC_CTL_EN__MSM_V),
|
||
|
baddr + AUX_CODEC_CTL_OFFSET);
|
||
|
}
|
||
|
}
|
||
|
mb();
|
||
|
}
|
||
|
|
||
|
/* Set who control aux pcm path: adsp or MSM */
|
||
|
void aux_codec_pcm_path_ctl_en(bool msm_adsp_en)
|
||
|
{
|
||
|
void __iomem *baddr = get_base_addr(&the_aux_pcm_state);
|
||
|
uint32_t val;
|
||
|
|
||
|
if (!IS_ERR(baddr)) {
|
||
|
val = readl(baddr + PCM_PATH_CTL_OFFSET);
|
||
|
if (msm_adsp_en) { /* adsp */
|
||
|
writel(
|
||
|
((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) |
|
||
|
PCM_PATH_CTL__ADSP_CTL_EN__ADSP_V),
|
||
|
baddr + PCM_PATH_CTL_OFFSET);
|
||
|
} else { /* MSM */
|
||
|
writel(
|
||
|
((val & ~PCM_PATH_CTL__ADSP_CTL_EN_BMSK) |
|
||
|
PCM_PATH_CTL__ADSP_CTL_EN__MSM_V),
|
||
|
baddr + PCM_PATH_CTL_OFFSET);
|
||
|
}
|
||
|
}
|
||
|
mb();
|
||
|
return;
|
||
|
}
|
||
|
EXPORT_SYMBOL(aux_codec_pcm_path_ctl_en);
|
||
|
|
||
|
int aux_pcm_gpios_request(void)
|
||
|
{
|
||
|
int rc = 0;
|
||
|
|
||
|
MM_DBG("aux_pcm_gpios_request\n");
|
||
|
rc = gpio_request(the_aux_pcm_state.dout, "AUX PCM DOUT");
|
||
|
if (rc) {
|
||
|
MM_ERR("GPIO request for AUX PCM DOUT failed\n");
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc = gpio_request(the_aux_pcm_state.din, "AUX PCM DIN");
|
||
|
if (rc) {
|
||
|
MM_ERR("GPIO request for AUX PCM DIN failed\n");
|
||
|
gpio_free(the_aux_pcm_state.dout);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc = gpio_request(the_aux_pcm_state.syncout, "AUX PCM SYNC OUT");
|
||
|
if (rc) {
|
||
|
MM_ERR("GPIO request for AUX PCM SYNC OUT failed\n");
|
||
|
gpio_free(the_aux_pcm_state.dout);
|
||
|
gpio_free(the_aux_pcm_state.din);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
rc = gpio_request(the_aux_pcm_state.clkin_a, "AUX PCM CLKIN A");
|
||
|
if (rc) {
|
||
|
MM_ERR("GPIO request for AUX PCM CLKIN A failed\n");
|
||
|
gpio_free(the_aux_pcm_state.dout);
|
||
|
gpio_free(the_aux_pcm_state.din);
|
||
|
gpio_free(the_aux_pcm_state.syncout);
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
EXPORT_SYMBOL(aux_pcm_gpios_request);
|
||
|
|
||
|
|
||
|
void aux_pcm_gpios_free(void)
|
||
|
{
|
||
|
MM_DBG(" aux_pcm_gpios_free \n");
|
||
|
|
||
|
/*
|
||
|
* Feed silence frames before close to prevent buzzing sound in BT at
|
||
|
* call end. This fix is applicable only to Marimba BT.
|
||
|
*/
|
||
|
gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 0, GPIO_CFG_OUTPUT,
|
||
|
GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
|
||
|
gpio_set_value(the_aux_pcm_state.dout, 0);
|
||
|
msleep(20);
|
||
|
gpio_tlmm_config(GPIO_CFG(the_aux_pcm_state.dout, 1, GPIO_CFG_OUTPUT,
|
||
|
GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
|
||
|
|
||
|
gpio_free(the_aux_pcm_state.dout);
|
||
|
gpio_free(the_aux_pcm_state.din);
|
||
|
gpio_free(the_aux_pcm_state.syncout);
|
||
|
gpio_free(the_aux_pcm_state.clkin_a);
|
||
|
}
|
||
|
EXPORT_SYMBOL(aux_pcm_gpios_free);
|
||
|
|
||
|
|
||
|
static int get_aux_pcm_gpios(struct platform_device *pdev)
|
||
|
{
|
||
|
int rc = 0;
|
||
|
struct resource *res;
|
||
|
|
||
|
/* Claim all of the GPIOs. */
|
||
|
res = platform_get_resource_byname(pdev, IORESOURCE_IO,
|
||
|
"aux_pcm_dout");
|
||
|
if (!res) {
|
||
|
MM_ERR("%s: failed to get gpio AUX PCM DOUT\n", __func__);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
the_aux_pcm_state.dout = res->start;
|
||
|
|
||
|
res = platform_get_resource_byname(pdev, IORESOURCE_IO,
|
||
|
"aux_pcm_din");
|
||
|
if (!res) {
|
||
|
MM_ERR("%s: failed to get gpio AUX PCM DIN\n", __func__);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
the_aux_pcm_state.din = res->start;
|
||
|
|
||
|
res = platform_get_resource_byname(pdev, IORESOURCE_IO,
|
||
|
"aux_pcm_syncout");
|
||
|
if (!res) {
|
||
|
MM_ERR("%s: failed to get gpio AUX PCM SYNC OUT\n", __func__);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
the_aux_pcm_state.syncout = res->start;
|
||
|
|
||
|
res = platform_get_resource_byname(pdev, IORESOURCE_IO,
|
||
|
"aux_pcm_clkin_a");
|
||
|
if (!res) {
|
||
|
MM_ERR("%s: failed to get gpio AUX PCM CLKIN A\n", __func__);
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
the_aux_pcm_state.clkin_a = res->start;
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
static int aux_pcm_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
int rc = 0;
|
||
|
struct resource *mem_src;
|
||
|
|
||
|
MM_DBG("aux_pcm_probe \n");
|
||
|
mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
||
|
"aux_codec_reg_addr");
|
||
|
if (!mem_src) {
|
||
|
rc = -ENODEV;
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
the_aux_pcm_state.aux_pcm_base = ioremap(mem_src->start,
|
||
|
(mem_src->end - mem_src->start) + 1);
|
||
|
if (!the_aux_pcm_state.aux_pcm_base) {
|
||
|
rc = -ENOMEM;
|
||
|
goto done;
|
||
|
}
|
||
|
rc = get_aux_pcm_gpios(pdev);
|
||
|
if (rc) {
|
||
|
MM_ERR("GPIO configuration failed\n");
|
||
|
rc = -ENODEV;
|
||
|
}
|
||
|
|
||
|
done: return rc;
|
||
|
|
||
|
}
|
||
|
|
||
|
static int aux_pcm_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
iounmap(the_aux_pcm_state.aux_pcm_base);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static struct platform_driver aux_pcm_driver = {
|
||
|
.probe = aux_pcm_probe,
|
||
|
.remove = aux_pcm_remove,
|
||
|
.driver = {
|
||
|
.name = "msm_aux_pcm",
|
||
|
.owner = THIS_MODULE,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static int __init aux_pcm_init(void)
|
||
|
{
|
||
|
|
||
|
return platform_driver_register(&aux_pcm_driver);
|
||
|
}
|
||
|
|
||
|
static void __exit aux_pcm_exit(void)
|
||
|
{
|
||
|
platform_driver_unregister(&aux_pcm_driver);
|
||
|
}
|
||
|
|
||
|
module_init(aux_pcm_init);
|
||
|
module_exit(aux_pcm_exit);
|
||
|
|
||
|
MODULE_DESCRIPTION("MSM AUX PCM driver");
|
||
|
MODULE_LICENSE("GPL v2");
|