soundwire: Initial version of soundwire master

Add soundwire master driver for soundwire master controller.
This driver initializes soundwire master controller
hardware and adds soundwire slave devices to the bus.
It handles all the interrupts generated by soundwire bus.
It also handles the port configuration of slave devices and
master for audio data transfer between master and slave ports.

Change-Id: Icf90e0268853c7b956e4c92f74af8dfb1efa0645
Signed-off-by: Sudheer Papothi <spapothi@codeaurora.org>
This commit is contained in:
Sudheer Papothi 2015-01-03 02:09:40 +05:30 committed by Gerrit - the friendly Code Review server
parent d21259ad72
commit 6c1f428522
5 changed files with 963 additions and 0 deletions

View file

@ -7,3 +7,11 @@ menuconfig SOUNDWIRE
Soundwire is a two wire interface for audio to connect
simple peripheral components in mobile devices.
if SOUNDWIRE
config SOUNDWIRE_WCD_CTRL
depends on WCD9335_CODEC
tristate "QTI WCD CODEC Soundwire controller"
default n
help
Select driver for QTI's Soundwire Master Component.
endif

View file

@ -2,3 +2,4 @@
# Makefile for kernel soundwire framework.
#
obj-$(CONFIG_SOUNDWIRE) += soundwire.o
obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr-wcd-ctrl.o

View file

@ -0,0 +1,695 @@
/* Copyright (c) 2015, 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/irq.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/soundwire/soundwire.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include "swrm_registers.h"
#include "swr-wcd-ctrl.h"
u8 mstr_ports[] = {100, 101, 102, 103, 104, 105, 106, 107};
#define SWR_MSTR_PORT_LEN ARRAY_SIZE(mstr_ports)
#define SWR_NUM_SLV_DEVICES 3 /* This includes dev_num_0 */
struct usecase uc[] = {
{0, 0, 0}, /* UC0: no ports */
{1, 1, 2400}, /* UC1: Spkr */
{1, 4, 600}, /* UC2: Compander */
{1, 2, 300}, /* UC3: Smart Boost */
{1, 2, 1200}, /* UC4: VI Sense */
{4, 9, 4500}, /* UC5: Spkr + Comp + SB + VI */
{8, 18, 9000}, /* UC6: 2*(Spkr + Comp + SB + VI) */
{2, 2, 4800}, /* UC7: 2*Spkr */
{2, 5, 3000}, /* UC8: Spkr + Comp */
{4, 10, 6000}, /* UC9: 2*(Spkr + Comp) */
};
#define MAX_USECASE ARRAY_SIZE(uc)
struct port_params pp[MAX_USECASE][SWR_MSTR_PORT_LEN] = {
/* UC 0 */
{
},
/* UC 1 */
{
{8, 1, 0},
},
/* UC 2 */
{
{32, 2, 0},
},
/* UC 3 */
{
{64, 12, 15},
},
/* UC 4 */
{
{16, 7, 0},
},
/* UC 5 */
{
{8, 1, 0},
{32, 2, 0},
{64, 12, 15},
{16, 7, 0},
},
/* UC 6 */
{
{8, 1, 0},
{32, 2, 0},
{64, 12, 15},
{16, 7, 0},
{8, 6, 0},
{32, 18, 0},
{64, 13, 15},
{16, 10, 0},
},
/* UC 7 */
{
{8, 1, 0},
{8, 6, 0},
},
/* UC 8 */
{
{8, 1, 0},
{32, 2, 0},
},
/* UC 9 */
{
{8, 1, 0},
{32, 2, 0},
{8, 6, 0},
{32, 18, 0},
},
};
static int swrm_get_port_config(struct swr_master *master)
{
u32 ch_rate = 0;
u32 num_ch = 0;
int i, uc_idx;
u32 portcount = 0;
for (i = 0; i < master->num_port; i++) {
if (master->port[i].port_en) {
ch_rate += master->port[i].ch_rate;
num_ch += master->port[i].num_ch;
portcount++;
}
}
for (i = 0; i < ARRAY_SIZE(uc); i++) {
if ((uc[i].num_port == portcount) &&
(uc[i].num_ch == num_ch) &&
(uc[i].chrate == ch_rate)) {
uc_idx = i;
break;
}
}
if (i >= ARRAY_SIZE(uc)) {
dev_err(&master->dev,
"%s: usecase port:%d, num_ch:%d, chrate:%d not found\n",
__func__, master->num_port, num_ch, ch_rate);
return -EINVAL;
}
for (i = 0; i < master->num_port; i++) {
if (master->port[i].port_en) {
master->port[i].sinterval = pp[uc_idx][i].si;
master->port[i].offset1 = pp[uc_idx][i].off1;
master->port[i].offset2 = pp[uc_idx][i].off2;
}
}
return 0;
}
static int swrm_get_master_port(u8 *mstr_port_id, u8 slv_port_id)
{
int i;
for (i = 0; i < SWR_MSTR_PORT_LEN; i++) {
if (mstr_ports[i] == slv_port_id) {
*mstr_port_id = i;
return 0;
}
}
return -EINVAL;
}
static u32 get_cmd_rd_fifo_depth(struct swr_mstr_ctrl *swrm)
{
return (swrm->read(swrm->handle, SWRM_COMP_PARAMS) &
SWRM_COMP_PARAMS_RD_FIFO_DEPTH) >> 15;
}
static int swrm_cmd_fifo_rd_cmd(struct swr_mstr_ctrl *swrm, u32 *cmd_data,
u8 dev_addr, u8 cmd_id, u16 reg_addr,
u32 len)
{
u32 val;
u32 fifo_cnt;
int ret = 0;
do {
fifo_cnt = ((swrm->read(swrm->handle, SWRM_CMD_FIFO_STATUS) &
SWRM_CMD_FIFO_STATUS_RD_CMD_FIFO_CNT_MASK) >> 16);
} while (fifo_cnt >= (get_cmd_rd_fifo_depth(swrm) - 1));
if (!cmd_id) {
cmd_id = swrm->rcmd_id;
swrm->rcmd_id = ((swrm->rcmd_id < 14) ? (cmd_id++) : 0);
}
val = (reg_addr | (cmd_id << 16) | (dev_addr << 20) |
(len << 24));
ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_RD_CMD, val);
if (ret < 0) {
dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n",
__func__, val, ret);
goto err;
}
*cmd_data = swrm->read(swrm->handle, SWRM_CMD_FIFO_RD_FIFO_ADDR);
err:
return ret;
}
static u32 get_cmd_wr_fifo_depth(struct swr_mstr_ctrl *swrm)
{
return (swrm->read(swrm->handle, SWRM_COMP_PARAMS) &
SWRM_COMP_PARAMS_WR_FIFO_DEPTH) >> 10;
}
static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data,
u8 dev_addr, u8 cmd_id, u16 reg_addr)
{
u32 val;
u32 fifo_cnt;
int ret = 0;
do {
fifo_cnt = ((swrm->read(swrm->handle, SWRM_CMD_FIFO_STATUS) &
SWRM_CMD_FIFO_STATUS_WR_CMD_FIFO_CNT_MASK) >> 8);
} while (fifo_cnt >= (get_cmd_wr_fifo_depth(swrm) - 1));
if (!cmd_id) {
cmd_id = swrm->wcmd_id;
swrm->wcmd_id = ((swrm->wcmd_id < 14) ? (cmd_id++) : 0);
}
val = (reg_addr | (cmd_id << 16) | (dev_addr << 20) |
(cmd_data << 24));
ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_WR_CMD, val);
if (ret < 0) {
dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n",
__func__, val, ret);
goto err;
}
if (cmd_id == 0xF)
wait_for_completion_timeout(&swrm->broadcast, (2 * HZ/10));
err:
return ret;
}
static int swrm_read(struct swr_master *master, u8 dev_num, u32 reg_addr,
u32 *buf, u32 len)
{
struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
if (dev_num) {
swrm_cmd_fifo_rd_cmd(swrm, buf, dev_num, 0, reg_addr,
len);
} else {
if (swrm->read)
buf[0] = swrm->read(swrm->handle, reg_addr);
else {
dev_err(&master->dev, "%s: handle is NULL 0x%x\n",
__func__, reg_addr);
return -EINVAL;
}
}
return 0;
}
static int swrm_write(struct swr_master *master, u8 dev_num, u32 reg_addr,
u32 *buf)
{
struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
int ret = 0;
if (dev_num) {
ret = swrm_cmd_fifo_wr_cmd(swrm, buf[0], dev_num, 0, reg_addr);
} else {
if (swrm->write) {
ret = swrm->write(swrm->handle, reg_addr, buf[0]);
} else {
dev_err(&master->dev, "%s: handle is NULL 0x%x\n",
__func__, reg_addr);
return -EINVAL;
}
}
return ret;
}
static u8 get_inactive_bank_num(struct swr_mstr_ctrl *swrm)
{
return (swrm->read(swrm->handle, SWRM_MCP_STATUS) &
SWRM_MCP_STATUS_BANK_NUM_MASK) ? 0 : 1;
}
static void enable_bank_switch(struct swr_mstr_ctrl *swrm, u8 bank,
u8 row, u8 col)
{
swrm_cmd_fifo_wr_cmd(swrm, ((row << 3) | col), 0xF, 0,
SWRS_SCP_FRAME_CTRL_BANK(bank));
}
static void disable_chs_next_bank(struct swr_master *master,
u8 bank, u8 *mport)
{
struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
struct swr_port_info *port;
int i;
u32 value;
for (i = 0; i < master->num_port; i++) {
port = &master->port[i];
swrm_cmd_fifo_wr_cmd(swrm, 0, port->dev_id, 0,
SWRS_DP_CHANNEL_ENABLE_BANK(i, bank));
value = ((swrm->read(swrm->handle,
SWRM_DP_PORT_CTRL_BANK((mport[i]+1), bank))) &
~SWRM_DP_PORT_CTRL_EN_CHAN_MASK);
swrm->write(swrm->handle,
SWRM_DP_PORT_CTRL_BANK((mport[i]+1), bank), value);
}
}
static void swrm_apply_port_config(struct swr_master *master, u8 *mport)
{
struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
u32 value;
struct swr_port_info *port;
u8 bank;
int i;
bank = get_inactive_bank_num(swrm);
dev_dbg(&master->dev, "%s: bank %d\n", __func__, bank);
for (i = 0; i < master->num_port; i++) {
port = &master->port[i];
swrm_cmd_fifo_wr_cmd(swrm, port->ch_en, port->dev_id, 0,
SWRS_DP_CHANNEL_ENABLE_BANK(i, bank));
swrm_cmd_fifo_wr_cmd(swrm, port->sinterval, port->dev_id, 0,
SWRS_DP_SAMPLE_CONTROL_1_BANK(i, bank));
swrm_cmd_fifo_wr_cmd(swrm, port->offset1, port->dev_id, 0,
SWRS_DP_OFFSET_CONTROL_1_BANK(i, bank));
swrm_cmd_fifo_wr_cmd(swrm, port->offset2, port->dev_id, 0,
SWRS_DP_OFFSET_CONTROL_2_BANK(i, bank));
swrm_cmd_fifo_wr_cmd(swrm, (SWR_MAX_COL-1), port->dev_id, 0,
SWRS_DP_HCONTROL_BANK(i, bank));
swrm_cmd_fifo_wr_cmd(swrm, 1, port->dev_id, 0,
SWRS_DP_BLOCK_CONTROL_3_BANK(i, bank));
value = ((port->ch_en)
<< SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
value |= ((port->offset2)
<< SWRM_DP_PORT_CTRL_OFFSET2_SHFT);
value |= ((port->offset1)
<< SWRM_DP_PORT_CTRL_OFFSET1_SHFT);
value |= port->sinterval;
swrm->write(swrm->handle,
SWRM_DP_PORT_CTRL_BANK((mport[i]+1), bank), value);
}
enable_bank_switch(swrm, bank, SWR_MAX_ROW, SWR_MAX_COL);
disable_chs_next_bank(master, !(!(!bank)), mport);
}
static int swrm_connect_port(struct swr_master *master,
struct swr_params *portinfo)
{
int i;
struct swr_port_info *port;
int ret = 0;
u8 mport[SWR_MSTR_PORT_LEN];
dev_dbg(&master->dev, "%s: enter\n", __func__);
if (!portinfo)
return -EINVAL;
for (i = 0; i < portinfo->num_port; i++) {
ret = swrm_get_master_port(&mport[i],
portinfo->port_id[i]);
if (ret < 0) {
dev_err(&master->dev,
"%s: mstr portid for slv port %d not found\n",
__func__, portinfo->port_id[i]);
return -EINVAL;
}
port = &master->port[i];
port->dev_id = portinfo->dev_id;
port->port_id = portinfo->port_id[i];
port->num_ch = portinfo->num_ch[i];
port->ch_rate = portinfo->ch_rate[i];
port->ch_en = portinfo->ch_en[i];
port->port_en = true;
dev_dbg(&master->dev,
"%s: port_id %d ch_rate %d num_ch %d ch_en %d\n",
__func__, port->port_id, port->ch_rate, port->num_ch,
port->ch_en);
}
master->num_port += portinfo->num_port;
if (master->num_port >= SWR_MSTR_PORT_LEN)
master->num_port = SWR_MSTR_PORT_LEN;
swrm_get_port_config(master);
swr_port_response(master, portinfo->tid);
swrm_apply_port_config(master, &mport[0]);
return 0;
}
static int swrm_disconnect_port(struct swr_master *master,
struct swr_params *portinfo)
{
int i;
struct swr_port_info *port;
u8 mport[SWR_MSTR_PORT_LEN];
int ret = 0;
if (!portinfo) {
dev_err(&master->dev, "%s: portinfo is NULL\n", __func__);
return -EINVAL;
}
for (i = 0; i < portinfo->num_port; i++) {
ret = swrm_get_master_port(&mport[i],
portinfo->port_id[i]);
if (ret < 0) {
dev_err(&master->dev,
"%s: mstr portid for slv port %d not found\n",
__func__, portinfo->port_id[i]);
return -EINVAL;
}
port = &master->port[i];
port->dev_id = portinfo->dev_id;
port->port_id = portinfo->port_id[i];
port->num_ch = portinfo->num_ch[i];
port->ch_rate = portinfo->ch_rate[i];
port->ch_en = portinfo->ch_en[i];
port->port_en = false;
}
if (master->num_port >= SWR_MSTR_PORT_LEN)
master->num_port = SWR_MSTR_PORT_LEN;
swrm_get_port_config(master);
master->num_port -= portinfo->num_port;
swr_port_response(master, portinfo->tid);
swrm_apply_port_config(master, &mport[0]);
return 0;
}
static irqreturn_t swr_mstr_interrupt(int irq, void *dev)
{
struct swr_mstr_ctrl *swrm = dev;
u32 value;
value = swrm->read(swrm->handle, SWRM_INTERRUPT_STATUS);
value &= SWRM_INTERRUPT_STATUS_RMSK;
swrm->write(swrm->handle, SWRM_INTERRUPT_CLEAR, value);
switch (value) {
case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ:
break;
case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED:
break;
case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS:
break;
case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET:
break;
case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW:
break;
case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW:
break;
case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW:
break;
case SWRM_INTERRUPT_STATUS_CMD_ERROR:
break;
case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION:
break;
case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH:
break;
case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED:
complete(&swrm->broadcast);
break;
case SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED:
break;
case SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED:
break;
case SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL:
break;
case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED:
complete(&swrm->reset);
break;
case SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED:
break;
default:
break;
}
return IRQ_HANDLED;
}
static int swrm_get_auto_enum_slaves(struct swr_mstr_ctrl *swrm)
{
int i;
u32 val, tval;
u32 status;
u32 device_id1[SWR_NUM_SLV_DEVICES];
u32 device_id2[SWR_NUM_SLV_DEVICES];
val = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS);
tval = val;
for (i = 0; i < SWR_NUM_SLV_DEVICES; i++) {
status = (val & SWRM_MCP_SLV_STATUS_MASK);
dev_dbg(swrm->dev, "%s slave %d status %d\n",
__func__, i, status);
if (status == 0x01)
swrm->num_enum_slaves++;
val = (val >> 2);
}
for (i = 1; i < SWR_NUM_SLV_DEVICES; i++) {
if (((tval >> 2) & SWRM_MCP_SLV_STATUS_MASK) == 0x01) {
device_id1[i] = swrm->read(swrm->handle,
SWRM_ENUMERATOR_SLAVE_DEV_ID_1(i));
device_id2[i] = swrm->read(swrm->handle,
SWRM_ENUMERATOR_SLAVE_DEV_ID_2(i));
dev_dbg(swrm->dev,
"%s: dev_id1 %d, dev_id2 %d of slave %d\n",
__func__, device_id1[i], device_id2[i], i);
}
}
return 0;
}
static int swrm_master_init(struct swr_mstr_ctrl *swrm)
{
int ret = 0;
u32 mask, val;
u8 row_ctrl = SWR_MAX_ROW;
u8 col_ctrl = SWR_MIN_COL;
u8 ping_val = 0;
u8 retry_cmd_num = 3;
/* Clear Rows and Cols */
mask = (SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK |
SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK);
val = swrm->read(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(0));
val &= (~mask);
val |= ((row_ctrl << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) |
(col_ctrl << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT));
swrm->write(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(0), val);
/* Clear all interrupts*/
swrm->write(swrm->handle, SWRM_INTERRUPT_MASK_ADDR, 0);
/* Set Auto enumeration flag */
swrm->write(swrm->handle, SWRM_ENUMERATOR_CFG_ADDR, 1);
/* Interrupt when Auto Enum Table is Full */
swrm->write(swrm->handle, SWRM_INTERRUPT_MASK_ADDR,
1 << SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_SHFT);
/* Configure NO_PINGS */
val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR);
val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK;
val |= (ping_val << SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT);
swrm->write(swrm->handle, SWRM_MCP_CFG_ADDR, val);
/* Configure number of retries of a read/write cmd */
val = swrm->read(swrm->handle, SWRM_CMD_FIFO_CFG_ADDR);
val &= ~SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_BMSK;
val |= (retry_cmd_num << SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT);
swrm->write(swrm->handle, SWRM_CMD_FIFO_CFG_ADDR, val);
/* Set IRQ LEVEL and enable*/
val = (swrm->read(swrm->handle,
SWRM_COMP_CFG_ADDR) | SWRM_COMP_CFG_RMSK);
swrm->write(swrm->handle, SWRM_COMP_CFG_ADDR, val);
/* Read the auto enum status */
swrm_get_auto_enum_slaves(swrm);
return ret;
}
static int swrm_probe(struct platform_device *pdev)
{
struct swr_mstr_ctrl *swrm;
struct swr_ctrl_platform_data *pdata;
int ret;
/* Allocate soundwire master driver structure */
swrm = kzalloc(sizeof(struct swr_mstr_ctrl), GFP_KERNEL);
if (!swrm) {
dev_err(&pdev->dev, "%s: no memory for swr mstr controller\n",
__func__);
ret = -ENOMEM;
goto err_memory_fail;
}
swrm->dev = &pdev->dev;
platform_set_drvdata(pdev, swrm);
swr_set_ctrl_data(&swrm->master, swrm);
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "%s: pdata from parent is NULL\n",
__func__);
ret = -EINVAL;
goto err_pdata_fail;
}
swrm->handle = (void *)pdata->handle;
swrm->read = pdata->read;
swrm->write = pdata->write;
swrm->clk = pdata->clk;
swrm->reg_irq = pdata->reg_irq;
swrm->master.read = swrm_read;
swrm->master.write = swrm_write;
swrm->master.connect_port = swrm_connect_port;
swrm->master.disconnect_port = swrm_disconnect_port;
swrm->master.dev.parent = &pdev->dev;
swrm->master.dev.of_node = pdev->dev.of_node;
swrm->master.num_port = 0;
swrm->num_enum_slaves = 0;
swrm->rcmd_id = 0;
swrm->wcmd_id = 0;
init_completion(&swrm->reset);
init_completion(&swrm->broadcast);
mutex_init(&swrm->mlock);
ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm,
SWR_IRQ_REGISTER);
if (ret) {
dev_err(&pdev->dev, "%s: IRQ register failed ret %d\n",
__func__, ret);
goto err_irq_fail;
}
ret = swr_register_master(&swrm->master);
if (ret) {
dev_err(&pdev->dev, "%s: error adding swr master\n", __func__);
goto err_mstr_fail;
}
if (pdev->dev.of_node)
of_register_swr_devices(&swrm->master);
/* Add devices registered with board-info as the
controller will be up now
*/
swr_master_add_boarddevices(&swrm->master);
mutex_lock(&swrm->mlock);
ret = swrm_master_init(swrm);
if (ret < 0) {
dev_err(&pdev->dev,
"%s: Error in master Initializaiton, err %d\n",
__func__, ret);
goto err_mstr_fail;
}
mutex_unlock(&swrm->mlock);
return 0;
err_mstr_fail:
swrm->reg_irq(swrm->handle, swr_mstr_interrupt,
swrm, SWR_IRQ_FREE);
err_irq_fail:
err_pdata_fail:
kfree(swrm);
err_memory_fail:
return ret;
}
static int swrm_remove(struct platform_device *pdev)
{
struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
swrm->reg_irq(swrm->handle, swr_mstr_interrupt,
swrm, SWR_IRQ_FREE);
swr_unregister_master(&swrm->master);
mutex_destroy(&swrm->mlock);
kfree(swrm);
return 0;
}
static struct of_device_id swrm_dt_match[] = {
{
.compatible = "qcom,swr-wcd",
},
{}
};
static struct platform_driver swr_mstr_driver = {
.probe = swrm_probe,
.remove = swrm_remove,
.driver = {
.name = SWR_WCD_NAME,
.owner = THIS_MODULE,
.of_match_table = swrm_dt_match,
},
};
static int __init swrm_init(void)
{
return platform_driver_register(&swr_mstr_driver);
}
subsys_initcall(swrm_init);
static void __exit swrm_exit(void)
{
platform_driver_unregister(&swr_mstr_driver);
}
module_exit(swrm_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("WCD SoundWire Controller");
MODULE_ALIAS("platform:swr-wcd");

View file

@ -0,0 +1,69 @@
/* Copyright (c) 2015, 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.
*/
#ifndef _SWR_WCD_CTRL_H
#define _SWR_WCD_CTRL_H
#include <linux/module.h>
#define SWR_MAX_ROW 48
#define SWR_MAX_COL 16
#define SWR_MIN_COL 2
#define SWR_WCD_NAME "swr-wcd"
enum {
SWR_IRQ_FREE,
SWR_IRQ_REGISTER,
};
struct usecase {
u8 num_port;
u8 num_ch;
u32 chrate;
};
struct port_params {
u8 si;
u8 off1;
u8 off2;
};
struct swr_ctrl_platform_data {
void *handle; /* holds priv data */
int (*read)(void *handle, int reg);
int (*write)(void *handle, int reg, int val);
int (*clk)(void *handle, bool enable);
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
void *data), void *swr_handle, int type);
};
struct swr_mstr_ctrl {
struct swr_master master;
struct device *dev;
struct resource *supplies;
struct clk *mclk;
struct completion reset;
struct completion broadcast;
struct mutex mlock;
u8 rcmd_id;
u8 wcmd_id;
void *handle; /* SWR Master handle from client for read and writes */
int (*read)(void *handle, int reg);
int (*write)(void *handle, int reg, int val);
int (*clk)(void *handle, bool enable);
int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
void *data), void *swr_handle, int type);
int irq;
int num_enum_slaves;
};
#endif /* _SWR_WCD_CTRL_H */

View file

@ -0,0 +1,190 @@
/* Copyright (c) 2015, 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.
*/
#ifndef _SWRM_REGISTERS_H
#define _SWRM_REGISTERS_H
#define SWRM_BASE_ADDRESS 0x00
#define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004)
#define SWRM_COMP_CFG_RMSK 0x3
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2
#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_SHFT 0x1
#define SWRM_COMP_CFG_ENABLE_BMSK 0x1
#define SWRM_COMP_CFG_ENABLE_SHFT 0x0
#define SWRM_COMP_PARAMS (SWRM_BASE_ADDRESS+0x100)
#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK 0x0000001F
#define SWRM_COMP_PARAMS_DIN_PORTS_MASK 0x000003E0
#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00
#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000
#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES 0x00F00000
#define SWRM_COMP_PARAMS_DATA_LANES 0x07000000
#define SWRM_INTERRUPT_STATUS (SWRM_BASE_ADDRESS+0x00000200)
#define SWRM_INTERRUPT_STATUS_RMSK 0x1FFFF
#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ 0x1
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED 0x2
#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS 0x4
#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET 0x8
#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x10
#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x20
#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW 0x40
#define SWRM_INTERRUPT_STATUS_CMD_ERROR 0x80
#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION 0x100
#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH 0x200
#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x400
#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED 0x800
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED 0x1000
#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL 0x2000
#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED 0x4000
#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x8000
#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x10000
#define SWRM_INTERRUPT_MASK_ADDR (SWRM_BASE_ADDRESS+0x00000204)
#define SWRM_INTERRUPT_MASK_RMSK 0x1FFFF
#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_BMSK 0x1
#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_SHFT 0x0
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_BMSK 0x2
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_SHFT 0x1
#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_BMSK 0x4
#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_SHFT 0x2
#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_BMSK 0x8
#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_SHFT 0x3
#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_BMSK 0x10
#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_SHFT 0x4
#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_BMSK 0x20
#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_SHFT 0x5
#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_BMSK 0x40
#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_SHFT 0x6
#define SWRM_INTERRUPT_MASK_CMD_ERROR_BMSK 0x80
#define SWRM_INTERRUPT_MASK_CMD_ERROR_SHFT 0x7
#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_BMSK 0x100
#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_SHFT 0x8
#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_BMSK 0x200
#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_SHFT 0x9
#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_BMSK 0x400
#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_SHFT 0xA
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_BMSK 0x800
#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_SHFT 0xB
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_BMSK 0x1000
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_SHFT 0xC
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_BMSK 0x2000
#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_SHFT 0xD
#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_BMSK 0x4000
#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_SHFT 0xE
#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_BMSK 0x8000
#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_SHFT 0xF
#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_BMSK 0x10000
#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_SHFT 0x10
#define SWRM_INTERRUPT_CLEAR (SWRM_BASE_ADDRESS+0x00000208)
#define SWRM_CMD_FIFO_WR_CMD (SWRM_BASE_ADDRESS + 0x00000300)
#define SWRM_CMD_FIFO_WR_CMD_MASK 0xFFFFFFFF
#define SWRM_CMD_FIFO_RD_CMD (SWRM_BASE_ADDRESS + 0x00000304)
#define SWRM_CMD_FIFO_RD_CMD_MASK 0xFFFFFFF
#define SWRM_CMD_FIFO_CMD (SWRM_BASE_ADDRESS + 0x00000308)
#define SWRM_CMD_FIFO_STATUS (SWRM_BASE_ADDRESS + 0x0000030C)
#define SWRM_CMD_FIFO_STATUS_WR_CMD_FIFO_CNT_MASK 0x1F00
#define SWRM_CMD_FIFO_STATUS_RD_CMD_FIFO_CNT_MASK 0x7C00000
#define SWRM_CMD_FIFO_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000314)
#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_BMSK 0x7
#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT 0x0
#define SWRM_CMD_FIFO_RD_FIFO_ADDR (SWRM_BASE_ADDRESS + 0x00000318)
#define SWRM_ENUMERATOR_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000500)
#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_BMSK 0x1
#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_SHFT 0x0
#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (SWRM_BASE_ADDRESS+0x530+0x8*m)
#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (SWRM_BASE_ADDRESS+0x534+0x8*m)
#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (SWRM_BASE_ADDRESS+0x101C+0x40*m)
#define SWRM_MCP_FRAME_CTRL_BANK_RMSK 0x000007ff
#define SWRM_MCP_FRAME_CTRL_BANK_SHFT 0
#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_BMSK 0x700
#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT 8
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK 0xF8
#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK 0x7
#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0
#define SWRM_MCP_BUS_CTRL_ADDR (SWRM_BASE_ADDRESS+0x00001044)
#define SWRM_MCP_BUS_CTRL_BUS_RESET_BMSK 0x1
#define SWRM_MCP_BUS_CTRL_BUS_RESET_SHFT 0x0
#define SWRM_MCP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00001048)
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK 0x3E0000
#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11
#define SWRM_MCP_STATUS (SWRM_BASE_ADDRESS+0x104C)
#define SWRM_MCP_STATUS_BANK_NUM_MASK 0x01
#define SWRM_MCP_SLV_STATUS (SWRM_BASE_ADDRESS+0x1090)
#define SWRM_MCP_SLV_STATUS_MASK 0x03
#define SWRM_DP_PORT_CTRL_BANK(n, m) (SWRM_BASE_ADDRESS + \
0x00001124 + \
0x100*(n-1) + \
0x40*m)
#define SWRM_DP_PORT_CTRL_BANK_MASK 0xFFFFFFFF
#define SWRM_DP_PORT_CTRL_EN_CHAN_MASK 0xFF000000
#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18
#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10
#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08
#define SWRM_DP_PORT_CTRL_SAMPLE_INTERVAL 0x00
/* Soundwire Slave Register definition */
#define SWRS_BASE_ADDRESS 0x00
#define SWRS_DP_REG_OFFSET(port, bank) ((0x100*port)+(0x10*bank))
#define SWRS_DP_CHANNEL_ENABLE_BANK(n, m) (SWRS_BASE_ADDRESS + 0x20 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_SAMPLE_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x22 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_OFFSET_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x24 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_OFFSET_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x25 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_HCONTROL_BANK(n, m) (SWRS_BASE_ADDRESS + 0x26 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_DP_BLOCK_CONTROL_3_BANK(n, m) (SWRS_BASE_ADDRESS + 0x27 + \
SWRS_DP_REG_OFFSET(n, m))
#define SWRS_SCP_FRAME_CTRL_BANK(m) (SWRS_BASE_ADDRESS + 0x60 + \
0x10*m)
#endif /* _SWRM_REGISTERS_H */