Files

594 lines
19 KiB
C
Executable File
Raw Permalink Blame History

/*----------------------------------------------------------------------------*
Copyright Statement:
This software/firmware and related documentation (<28><>EcoNet Software<72><65>)
are protected under relevant copyright laws. The information contained herein
is confidential and proprietary to EcoNet (HK) Limited (<28><>EcoNet<65><74>) and/or
its licensors. Without the prior written permission of EcoNet and/or its licensors,
any reproduction, modification, use or disclosure of EcoNet Software, and
information contained herein, in whole or in part, shall be strictly prohibited.
EcoNet (HK) Limited EcoNet. ALL RIGHTS RESERVED.
BY OPENING OR USING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY
ACKNOWLEDGES AND AGREES THAT THE SOFTWARE/FIRMWARE AND ITS
DOCUMENTATIONS (<28><>ECONET SOFTWARE<52><45>) RECEIVED FROM ECONET
AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON AN <20><>AS IS<49><53>
BASIS ONLY. ECONET EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
OR NON-INFRINGEMENT. NOR DOES ECONET PROVIDE ANY WARRANTY
WHATSOEVER WITH RESPECT TO THE SOFTWARE OF ANY THIRD PARTIES WHICH
MAY BE USED BY, INCORPORATED IN, OR SUPPLIED WITH THE ECONET SOFTWARE.
RECEIVER AGREES TO LOOK ONLY TO SUCH THIRD PARTIES FOR ANY AND ALL
WARRANTY CLAIMS RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
THAT IT IS RECEIVER<45><52>S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD
PARTY ALL PROPER LICENSES CONTAINED IN ECONET SOFTWARE.
ECONET SHALL NOT BE RESPONSIBLE FOR ANY ECONET SOFTWARE RELEASES
MADE TO RECEIVER<45><52>S SPECIFICATION OR CONFORMING TO A PARTICULAR
STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND
ECONET'S ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE ECONET
SOFTWARE RELEASED HEREUNDER SHALL BE, AT ECONET'S SOLE OPTION, TO
REVISE OR REPLACE THE ECONET SOFTWARE AT ISSUE OR REFUND ANY SOFTWARE
LICENSE FEES OR SERVICE CHARGES PAID BY RECEIVER TO ECONET FOR SUCH
ECONET SOFTWARE.
*---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
*
* $Author: $
* $Date: 2020/06/12 $
* $RCSfile: ecnt_i2s.c $
* $Revision: #0 $
*
*---------------------------------------------------------------------------*/
//---------------------------------------------------------------------------
// Include files
//---------------------------------------------------------------------------
#include <asm/io.h>
#include <linux/types.h>
#include <linux/of_irq.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <modules/i2s_global.h>
//#define I2S_LOOPBACK_TEST
//---------------------------------------------------------------------------
// Macro definitions
//---------------------------------------------------------------------------
#define I2S_ADDR_BASE1 (ecnt_i2s->base1)
#define I2S_ADDR_BASE2 (ecnt_i2s->base2)
/* I2S Register Base1 Group */
#define AFE_DAC_CON0 (I2S_ADDR_BASE1 + 0x000)
#define AFE_DAC_CON1 (I2S_ADDR_BASE1 + 0x004)
#define AFE_MEMIF_BURST_CFG (I2S_ADDR_BASE1 + 0x008)
#define AFE_MEMIF_BUF_MON1 (I2S_ADDR_BASE1 + 0x01c)
#define AFE_MEMIF_BUF_MON6 (I2S_ADDR_BASE1 + 0x034)
#define ETDM_COWORK_CON0 (I2S_ADDR_BASE1 + 0x04c)
#define ETDM_COWORK_CON1 (I2S_ADDR_BASE1 + 0x050)
#define ETDM_IN1_CON0 (I2S_ADDR_BASE1 + 0x05c)
#define ETDM_IN1_CON1 (I2S_ADDR_BASE1 + 0x060)
#define ETDM_IN1_CON2 (I2S_ADDR_BASE1 + 0x064)
#define ETDM_IN1_CON3 (I2S_ADDR_BASE1 + 0x068)
#define ETDM_IN1_CON4 (I2S_ADDR_BASE1 + 0x06c)
#define ETDM_IN1_CON5 (I2S_ADDR_BASE1 + 0x070)
#define ETDM_IN1_CON6 (I2S_ADDR_BASE1 + 0x074)
#define ETDM_IN1_MONITOR (I2S_ADDR_BASE1 + 0x078)
#define ETDM_OUT1_CON0 (I2S_ADDR_BASE1 + 0x07c)
#define ETDM_OUT1_CON1 (I2S_ADDR_BASE1 + 0x080)
#define ETDM_OUT1_CON2 (I2S_ADDR_BASE1 + 0x084)
#define ETDM_OUT1_CON3 (I2S_ADDR_BASE1 + 0x088)
#define ETDM_OUT1_CON4 (I2S_ADDR_BASE1 + 0x08c)
#define ETDM_OUT1_CON6 (I2S_ADDR_BASE1 + 0x094)
#define ETDM_OUT1_CON7 (I2S_ADDR_BASE1 + 0x098)
#define ETDM_OUT1_MONITOR (I2S_ADDR_BASE1 + 0x09c)
#define AFE_DL1_CHK_SUM1 (I2S_ADDR_BASE1 + 0x0a0)
#define AFE_DL1_CHK_SUM2 (I2S_ADDR_BASE1 + 0x0a4)
#define AFE_DL1_BASE (I2S_ADDR_BASE1 + 0x0a8)
#define AFE_DL1_CUR (I2S_ADDR_BASE1 + 0x0ac)
#define AFE_DL1_END (I2S_ADDR_BASE1 + 0x0b0)
#define AFE_DL1_CON0 (I2S_ADDR_BASE1 + 0x0b4)
#define AFE_UL1_CHK_SUM1 (I2S_ADDR_BASE1 + 0x0bc)
#define AFE_UL1_CHK_SUM2 (I2S_ADDR_BASE1 + 0x0c0)
#define AFE_UL1_BASE (I2S_ADDR_BASE1 + 0x0c4)
#define AFE_UL1_END (I2S_ADDR_BASE1 + 0x0c8)
#define AFE_UL1_CUR (I2S_ADDR_BASE1 + 0x0cc)
#define AFE_UL1_CON0 (I2S_ADDR_BASE1 + 0x0d0)
#define AFE_SIDEBAND0 (I2S_ADDR_BASE1 + 0x0dc)
#define AFE_SIDEBAND1 (I2S_ADDR_BASE1 + 0x0e0)
#define AFE_IRQ_CON0 (I2S_ADDR_BASE1 + 0x0e4)
#define AFE_IRQ_CNT (I2S_ADDR_BASE1 + 0x0e8)
#define AFE_IRQ_CNT_MON (I2S_ADDR_BASE1 + 0x0ec)
#define AFE_IRQ_MON0 (I2S_ADDR_BASE1 + 0x0f0)
#define AFE_IRQ_MON1 (I2S_ADDR_BASE1 + 0x0f4)
#define AFE_IRQ_STS (I2S_ADDR_BASE1 + 0x0f8)
/* I2S Register Base2 Group */
#define AFE_IRQ1_CON0 (I2S_ADDR_BASE2 + 0x0100)
#define AFE_IRQ1_CNT (I2S_ADDR_BASE2 + 0x0104)
#define AFE_IRQ1_CNT_MON (I2S_ADDR_BASE2 + 0x0108)
#define AFE_IRQ1_MON0 (I2S_ADDR_BASE2 + 0x010c)
#define AFE_IRQ1_MON1 (I2S_ADDR_BASE2 + 0x0110)
#define ECNT_IOWRITE32(Reg, ONE_BITS_SUM, LEFT_SHIFT_NUM, NEW_BITS_SUM) \
iowrite32(((ioread32(Reg) & (~(ONE_BITS_SUM<<LEFT_SHIFT_NUM))) | \
(NEW_BITS_SUM << LEFT_SHIFT_NUM)) , Reg)
//---------------------------------------------------------------------------
// Type definitions
//---------------------------------------------------------------------------
struct ecnt_i2s {
struct device *dev;
void __iomem *base1;
void __iomem *base2;
int irq;
};
//---------------------------------------------------------------------------
// Global Variables
//---------------------------------------------------------------------------
struct ecnt_i2s *ecnt_i2s = NULL;
//---------------------------------------------------------------------------
// Static Variables
//---------------------------------------------------------------------------
static const struct of_device_id ecnt_i2s_of_id[] = {
{ .compatible = "econet,ecnt-i2s"},
{ /* sentinel */}
};
MODULE_DEVICE_TABLE(of, ecnt_i2s_of_id);
//---------------------------------------------------------------------------
// Function Declarations
//---------------------------------------------------------------------------
void (*ecnt_i2s_isr_func)(void);
//---------------------------------------------------------------------------
// Static Functions
//---------------------------------------------------------------------------
/* don't EXPORT this function. Create API for your purpose instead. */
static u32 get_i2s_data(u32 reg)
{
return readl(reg);
}
/* don't EXPORT this function. Create API for your purpose instead. */
static void set_i2s_data(u32 val, u32 reg)
{
writel(val, reg);
}
//---------------------------------------------------------------------------
// Exported Functions
//---------------------------------------------------------------------------
/* get_afe_irq_mon
* AFE_IRQ_MON0 : regiter for monitoring irq_b counter in rising edge
* and in falling edge
* AFE_IRQ_MON1 : register for irq0 miss flag, which means miss handle irq0 flag
* AFE_IRQ1_MON0 : same as IRQ_MON0, but for IRQ1
* AFE_IRQ1_MON1 : same as IRQ_MON1, but for IRQ1
*
* @param uint8_t irq_reg_num: 0 means IRQ0 1 means IRQ1
* uint8_t mon_num: 0 means MON0 1 means MON1
* @return void
*/
static u32 get_i2s_data_common(u32 reg)
{
if(reg < 0x100)
return readl(ecnt_i2s->base1 + reg);
else
return readl(ecnt_i2s->base2 + reg);
}
/* don't EXPORT this function. Create API for your purpose instead. */
static void set_i2s_data_common(u32 val, u32 reg)
{
if(reg < 0x100)
writel(val, ecnt_i2s->base1 + reg);
else
writel(val, ecnt_i2s->base2 + reg);
}
uint32_t read_i2s_reg(u32 reg)
{
return get_i2s_data_common(reg);
}
EXPORT_SYMBOL(read_i2s_reg);
uint32_t write_i2s_reg(u32 val, u32 reg)
{
set_i2s_data_common(val, reg);
}
EXPORT_SYMBOL(write_i2s_reg);
uint32_t i2s_io_write(u32 Reg, u32 one_bits_sum,u32 left_shift_num, u32 new_bits_sum)
{
set_i2s_data_common(((get_i2s_data_common(Reg) & (~(one_bits_sum<<left_shift_num))) |
(new_bits_sum << left_shift_num)) , Reg);
}
EXPORT_SYMBOL(i2s_io_write);
uint32_t get_afe_irq_mon(uint8_t irq_reg_num, uint8_t mon_num)
{
if(irq_reg_num == 0 && mon_num == 0)
return get_i2s_data(AFE_IRQ_MON0);
else if(irq_reg_num == 0 && mon_num == 1)
return get_i2s_data(AFE_IRQ_MON1);
else if(irq_reg_num == 1 && mon_num == 0)
return get_i2s_data(AFE_IRQ1_MON0);
else if(irq_reg_num == 1 && mon_num == 1)
return get_i2s_data(AFE_IRQ1_MON1);
else{
printk("[error] wrong irq_reg_num and mon_num combination!\n");
}
return 0;
}
EXPORT_SYMBOL(get_afe_irq_mon);
void i2s_write_afe_dl1_base(dma_addr_t afe_dl1_base_addr)
{
set_i2s_data(afe_dl1_base_addr, AFE_DL1_BASE);
}
EXPORT_SYMBOL(i2s_write_afe_dl1_base);
void i2s_write_afe_dl_end(dma_addr_t afe_dl1_end_addr)
{
set_i2s_data(afe_dl1_end_addr, AFE_DL1_END);
}
EXPORT_SYMBOL(i2s_write_afe_dl_end);
void i2s_write_afe_ul1_base(dma_addr_t afe_ul1_base_addr)
{
set_i2s_data(afe_ul1_base_addr, AFE_UL1_BASE);
}
EXPORT_SYMBOL(i2s_write_afe_ul1_base);
void i2s_write_afe_ul1_end(dma_addr_t afe_ul1_end_addr)
{
set_i2s_data(afe_ul1_end_addr, AFE_UL1_END);
}
EXPORT_SYMBOL(i2s_write_afe_ul1_end);
/* etdm_in1_slave_sel
* Select etdmin1 slave mode source
* Bitfield: ETDM_COWORK_CON0[27:24] = 0x8
* 8: from etdmout1 master
* 9: from etdmut1 slave
* @param void
* @return void
*/
void etdm_in1_slave_sel(uint8_t etdmSrc)
{
ECNT_IOWRITE32(ETDM_COWORK_CON0, 15, 24, 8);
}
EXPORT_SYMBOL(etdm_in1_slave_sel);
/* etdm_in1_sdata0_sel
* Select etdemin1 sdata0 source
* Bitfield: ETDM_COWORK_CON1[3:0] = 0x8
* 8: from etdmout1
* 10: from etdmout2
* @param void
* @return void
*/
void etdm_in1_sdata0_sel(uint8_t etdmSdataSrc)
{
ECNT_IOWRITE32(ETDM_COWORK_CON1, 15, 0, 8);
}
EXPORT_SYMBOL(etdm_in1_sdata0_sel);
/* set_irq_count
* After interrupt is enabled, irq_count will be decreased to 0,
* and then interrupt handler will be triggered
* Bitfield: AFE_IRQ_CNT[31:0] = 0x8000 frames
* If there is 2 channels within a frame, then the transmission length of
* 1 frame = 2 * 4 bytes. That is to say, 0x8000 frames = 256 KBytes
* @param void
* @return void
*/
void set_irq_count(uint32_t irq_count, uint8_t irq_idx)
{
if(irq_idx == 0)
set_i2s_data(irq_count , AFE_IRQ_CNT);
else if(irq_idx == 1)
set_i2s_data(irq_count , AFE_IRQ1_CNT);
else
printk("[error] wrong irq index!\n");
}
EXPORT_SYMBOL(set_irq_count);
uint32_t get_irq_count(uint8_t irq_idx)
{
if(irq_idx == 0)
return get_i2s_data( AFE_IRQ_CNT);
else if(irq_idx == 1)
return get_i2s_data(AFE_IRQ1_CNT);
else{
printk("[error] wrong irq index!\n");
return 0;
}
}
EXPORT_SYMBOL(get_irq_count);
void i2s_interrupt_init(void (*isr_func)(void))
{
if(isr_func != NULL)
ecnt_i2s_isr_func = isr_func;
}
EXPORT_SYMBOL(i2s_interrupt_init);
/* get_irq_status
*
* @param void
* @return void
*/
uint32_t get_irq_status(void)
{
return get_i2s_data(AFE_IRQ_STS);
}
EXPORT_SYMBOL(get_irq_status);
/* get_irq_ctrl_0_status
*
* @param void
* @return void
*/
uint32_t get_irq_ctrl_status(uint8_t irq_reg_index)
{
if(irq_reg_index == 0)
return get_i2s_data(AFE_IRQ_CON0);
else if (irq_reg_index == 1)
return get_i2s_data(AFE_IRQ1_CON0);
else{
printk("[error] wrong irq register index!\n");
return 0;
}
}
EXPORT_SYMBOL(get_irq_ctrl_status);
void I2S_Clear_Interrupt(void)
{
/* Clear IRQ */
printk("\n Clear IRQ0 status.\n");
ECNT_IOWRITE32(AFE_IRQ_CON0, 0x1, 2, 0x1);
ECNT_IOWRITE32(AFE_IRQ_CON0, 0x1, 2, 0x0);
printk(" Clear IRQ1 status.\n");
ECNT_IOWRITE32(AFE_IRQ1_CON0, 0x1, 2, 0x1);
ECNT_IOWRITE32(AFE_IRQ1_CON0, 0x1, 2, 0x0);
/* Clear miss flag */
printk(" Clear IRQ0 miss flag.\n");
ECNT_IOWRITE32(AFE_IRQ_CON0, 0x1, 3, 0x1);
ECNT_IOWRITE32(AFE_IRQ_CON0, 0x1, 3, 0x0);
printk(" Clear IRQ1 miss flag.\n");
ECNT_IOWRITE32(AFE_IRQ1_CON0, 0x1, 3, 0x1);
ECNT_IOWRITE32(AFE_IRQ1_CON0, 0x1, 3, 0x0);
}
EXPORT_SYMBOL(I2S_Clear_Interrupt);
void i2s_global_reset(void)
{
u32 val = GET_SCU_RST_RG();
SET_SCU_RST_RG(val | (0x1 << 10) ); //set 1
SET_SCU_RST_RG(val & (~(0x1 << 10))); //set 0
}
EXPORT_SYMBOL(i2s_global_reset);
/* I2sLoopbackTest_HwTrig_Control
* Enable or disable I2S module.
* Procedure:
* 5. Enable MEMIF agent
* 5-1) Enable downlink1(TX) memory path
* AFE_DAC_CON0[17] = 0x1
* 5-2) Enable uplink1(RX) memory path
* AFE_DAC_CON0[1] = 0x1
* 5-3) Enable the whole AFE module
* AFE_DAC_CON0[0] = 0x1
*
* 6. Enable ETDM
* 6-1) Enable ETDM Out
* ETDM_OUT1_CON0[0] = 0x1
* 6-2) Enable ETDM In
* ETDM_IN1_CON0[0] = 0x1
*
* @param void
* @return void
*/
void I2sLoopbackTest_HwTrig_Control(uint8_t isEnable)
{
if(isEnable == 1)
printk("\n I2S hardware enable \n");
else if(isEnable == 0)
printk(" I2S hardware disable \n");
else
printk(" [Error] wrong I2S hardware control parameter \n");
ECNT_IOWRITE32(AFE_DAC_CON0 , 0x1, 17, isEnable);
ECNT_IOWRITE32(AFE_DAC_CON0 , 0x1, 1 , isEnable);
ECNT_IOWRITE32(AFE_DAC_CON0 , 0x1, 0 , isEnable);
ECNT_IOWRITE32(ETDM_OUT1_CON0, 0x1, 0 , isEnable);
ECNT_IOWRITE32(ETDM_IN1_CON0 , 0x1, 0 , isEnable);
}
EXPORT_SYMBOL(I2sLoopbackTest_HwTrig_Control);
void I2S_Etdm_Cfg0(ETDM_CFG0 in1, ETDM_CFG0 out1)
{
set_i2s_data(out1.word, ETDM_OUT1_CON0);
set_i2s_data(in1.word, ETDM_IN1_CON0);
}
EXPORT_SYMBOL(I2S_Etdm_Cfg0);
void I2S_Etdm_Cfg1(ETDM_CFG1 in1, ETDM_CFG1 out1)
{
in1.bits.initial_count = 0xe;
in1.bits.initial_point = 0xe;
in1.bits.lrck_auto_off = 0x1;
in1.bits.chen_sel = 0x1;
in1.bits.lr_align = 0x1;
out1.bits.initial_count = 0xe;
out1.bits.initial_point = 0xe;
out1.bits.lrck_auto_off = 0x1;
out1.bits.chen_sel = 0x1;
set_i2s_data(out1.word, ETDM_OUT1_CON1);
set_i2s_data(in1.word, ETDM_IN1_CON1);
}
EXPORT_SYMBOL(I2S_Etdm_Cfg1);
void I2S_AFE_Cfg0_DL1(AFE_DL1_CFG0 dl1)
{
dl1.bits.hd_audio_on = 0x1;
dl1.bits.axi_maxLen = 0x1;
dl1.bits.pbuf_size = 0x3;
set_i2s_data(dl1.word, AFE_DL1_CON0);
}
EXPORT_SYMBOL(I2S_AFE_Cfg0_DL1);
void I2S_AFE_Cfg0_UL1(AFE_UL1_CFG0 ul1)
{
ul1.bits.hd_audio_on = 0x1;
ul1.bits.axi_maxLen = 0x1;
ul1.bits.force_no_mask = 0x1;
set_i2s_data(ul1.word, AFE_UL1_CON0);
}
EXPORT_SYMBOL(I2S_AFE_Cfg0_UL1);
void I2S_Afe_Irq_Cfg0(AFE_IRQ_CFG0 afe_irq, uint8_t irq_reg_sel)
{
if(irq_reg_sel == 0){
set_i2s_data(afe_irq.word, AFE_IRQ_CON0);
}else if(irq_reg_sel == 1){
set_i2s_data(afe_irq.word, AFE_IRQ1_CON0);
}else{
printk("[Error] wrong irq_reg_sel number! \n");
}
}
EXPORT_SYMBOL(I2S_Afe_Irq_Cfg0);
static irqreturn_t i2s_interrupt_top_half_handler (
int irq, void * dev_id )
{
if(ecnt_i2s_isr_func != NULL)
ecnt_i2s_isr_func();
return IRQ_HANDLED;
}
struct device* get_i2s_dev(void)
{
if ((ecnt_i2s) && (ecnt_i2s->dev))
return ecnt_i2s->dev;
else{
printk("[i2s] get_i2s_dev failed !\n");
return NULL;
}
}
EXPORT_SYMBOL(get_i2s_dev);
int get_i2s_irq(void)
{
return ecnt_i2s->irq;
}
EXPORT_SYMBOL(get_i2s_irq);
static int ecnt_i2s_drv_probe(struct platform_device *pdev)
{
struct resource *res = NULL;
printk("[i2s] ecnt_i2s_drv_probe\n");
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "No i2s DT node found");
return -EINVAL;
}
/* I2S allocate memory */
ecnt_i2s = devm_kzalloc(&pdev->dev, sizeof(struct ecnt_i2s), GFP_KERNEL);
if (!ecnt_i2s)
return -ENOMEM;
platform_set_drvdata(pdev, ecnt_i2s);
/* get irq num */
ecnt_i2s->irq = platform_get_irq(pdev, 0);
#ifdef I2S_LOOPBACK_TEST
res = devm_request_irq(&pdev->dev, ecnt_i2s->irq, i2s_interrupt_top_half_handler,
0, dev_name(&pdev->dev), ecnt_i2s);
if(res){
dev_err(&(pdev->dev), "devm_request_irq failed with err %d\n", res);
return -EINVAL;
}
printk("[i2s] ecnt_i2s->irq: %d\n", ecnt_i2s->irq);
#endif
/* get i2s base1 address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ecnt_i2s->dev = &pdev->dev;
ecnt_i2s->base1 = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ecnt_i2s->base1))
return PTR_ERR(ecnt_i2s->base1);
#if 0
printk("[i2s] res->name:%s\n", res->name);
printk("[i2s] res->start:0x%llx ===\n", res->start);
printk("[i2s] res->end:0x%llx ===\n", res->end);
printk("[i2s] ecnt_i2s->base1:0x%lx\n", (unsigned long)ecnt_i2s->base1);
#endif
/* get i2s base2 address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
ecnt_i2s->base2 = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(ecnt_i2s->base2))
return PTR_ERR(ecnt_i2s->base2);
#if 0
printk("[i2s] res->name:%s\n", res->name);
printk("[i2s] res->start:0x%llx ===\n", res->start);
printk("[i2s] res->end:0x%llx ===\n", res->end);
printk("[i2s] ecnt_i2s->base2:0x%lx\n", (unsigned long)ecnt_i2s->base2);
#endif
return 0;
}
static int ecnt_i2s_drv_remove(struct platform_device *pdev)
{
printk("[i2s] ecnt_i2s_drv_remove\n");
return 0;
}
/************************************************************************
* P L A T F O R M D R I V E R S D E C L A R A T I O N S
*************************************************************************
*/
static struct platform_driver ecnt_i2s_driver = {
.probe = ecnt_i2s_drv_probe,
.remove = ecnt_i2s_drv_remove,
.driver = {
.name = "ecnt-i2s",
.of_match_table = ecnt_i2s_of_id
},
};
module_platform_driver(ecnt_i2s_driver);
MODULE_DESCRIPTION("EcoNet i2s Driver");