static void spdif_snd_txctrl(struct rockchip_spdif_info *spdif, int on)
{
	void __iomem *regs = spdif->regs;
	u32 opr, xfer;

	RK_SPDIF_DBG("Entered %s\n", __func__);

	xfer = readl(regs + XFER) & XFER_MASK;
	opr = readl(regs + DMACR) & DMACR_TRAN_DMA_MASK
		& (~DMACR_TRAN_DMA_CTL_MASK);

	if (on) {
		xfer |= XFER_TRAN_START;
		opr |= DMACR_TRAN_DMA_ENABLE;
		writel(xfer, regs + XFER);
		writel(opr, regs + DMACR);
		RK_SPDIF_DBG("on xfer=0x%x,opr=0x%x\n", readl(
			regs + XFER), readl(regs + DMACR));
	} else {
		xfer &= ~XFER_TRAN_START;
		opr &= ~DMACR_TRAN_DMA_ENABLE;
		writel(xfer, regs + XFER);
		writel(opr, regs + DMACR);
		writel(1<<7, regs + CFGR);
		RK_SPDIF_DBG("off xfer=0x%x,opr=0x%x\n", readl(
			regs + XFER), readl(regs + DMACR));
	}
}
static int spdif_trigger(struct snd_pcm_substream *
		substream, int cmd, struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct rockchip_spdif_info *spdif = to_info(rtd->cpu_dai);
	unsigned long flags;

	RK_SPDIF_DBG("Entered %s\n", __func__);

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		spin_lock_irqsave(&spdif->lock, flags);
		spdif_snd_txctrl(spdif, 1);
		spin_unlock_irqrestore(&spdif->lock, flags);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		spin_lock_irqsave(&spdif->lock, flags);
		spdif_snd_txctrl(spdif, 0);
		spin_unlock_irqrestore(&spdif->lock, flags);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}
static int __init rk_spdif_init(void)
{
	int ret;
	
	RK_SPDIF_DBG("Entered %s\n", __func__);
	
	rk_snd_spdif_dit_device = platform_device_alloc("spdif-dit", -1);
	if (!rk_snd_spdif_dit_device){
		printk("spdif:platform_device_alloc spdif-dit\n");
		return -ENOMEM;
	}

	ret = platform_device_add(rk_snd_spdif_dit_device);
	if (ret)
		goto err1;

	rk_snd_spdif_device = platform_device_alloc("soc-audio", -4);
	if (!rk_snd_spdif_device) {
		printk("spdif:platform_device_alloc rk_soc-audio\n");
		ret = -ENOMEM;
		goto err2;
	}
	
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37))
	platform_set_drvdata(rk_snd_spdif_device, &rk_spdif);
#else
	platform_set_drvdata(rk_snd_spdif_device, &rk_spdif);
	rk_spdif.dev = &rk_snd_spdif_device->dev;
#endif

	//platform_set_drvdata(rk_snd_spdif_device, &rk_spdif);

	ret = platform_device_add(rk_snd_spdif_device);
	if (ret)
		goto err3;
	
	RK_SPDIF_DBG("rk_spdif_init ok\n");
	return ret;
err3:
	platform_device_put(rk_snd_spdif_device);
err2:
	platform_device_del(rk_snd_spdif_dit_device);
err1:
	platform_device_put(rk_snd_spdif_dit_device);
	
	return ret;
}
static int spdif_remove(struct platform_device *pdev)
{
	RK_SPDIF_DBG("Entered %s\n", __func__);

	rockchip_pcm_platform_unregister(&pdev->dev);
	snd_soc_unregister_component(&pdev->dev);

	return 0;
}
static int spdif_set_syclk(struct snd_soc_dai *
	cpu_dai, int clk_id, unsigned int freq, int dir)
{
	struct rockchip_spdif_info *spdif = to_info(cpu_dai);

	RK_SPDIF_DBG("Entered %s sysclk=%d\n", __func__, freq);

	spdif->clk_rate = freq;
	clk_set_rate(spdif->clk, freq);

	return 0;
}
static int rk_hw_params(struct snd_pcm_substream *substream,
		struct snd_pcm_hw_params *params)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
	unsigned long pll_out, rclk_rate;
	int ret, ratio;

  RK_SPDIF_DBG("spdif:Entered %s\n", __func__);
  
	switch (params_rate(params)) {
	case 44100:
		pll_out = 11289600;
		break;
	case 32000:
		pll_out = 8192000;
		break;
	case 48000:
		pll_out = 12288000;
		break;
	case 96000:
		pll_out = 24576000;
		break;
	default:
		printk("rk_spdif: params not support\n");
		return -EINVAL;
	}

	ratio = 256;
	rclk_rate = params_rate(params) * ratio;

	/* Set audio source clock rates */
	ret = set_audio_clock_rate(pll_out, rclk_rate);
	if (ret < 0)
		return ret;

	/* Set S/PDIF uses internal source clock */
	//ret = snd_soc_dai_set_sysclk(cpu_dai, SND_SOC_SPDIF_INT_MCLK,
					//rclk_rate, SND_SOC_CLOCK_IN);
	//if (ret < 0)
		//return ret;

	return ret;
}
static int spdif_probe(struct platform_device *pdev)
{
	/*struct device_node *spdif_np = pdev->dev.of_node;*/
	struct resource *memregion;
	struct resource *mem_res;
	struct rockchip_spdif_info *spdif;
	int ret;

	RK_SPDIF_DBG("Entered %s\n", __func__);

	spdif = devm_kzalloc(&pdev->dev, sizeof(
		struct rockchip_spdif_info), GFP_KERNEL);
	if (!spdif) {
		dev_err(&pdev->dev, "Can't allocate spdif info\n");
		return -ENOMEM;
	}
	platform_set_drvdata(pdev, spdif);

	spin_lock_init(&spdif->lock);

	/* get spdif register regoin. */
	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!mem_res) {
		dev_err(&pdev->dev, "No memory resource\n");
		ret = -ENOENT;
		goto err_;
	}
	memregion = devm_request_mem_region(&pdev->
			dev, mem_res->start,
			resource_size(mem_res), "rockchip-spdif");
	if (!memregion) {
		dev_err(&pdev->dev, "Memory region already claimed\n");
		ret = -EBUSY;
		goto err_;
	}
	spdif->regs = devm_ioremap(&pdev->dev, memregion->
			start, resource_size(memregion));
	if (!spdif->regs) {
		dev_err(&pdev->dev, "ioremap failed\n");
		ret = -ENOMEM;
		goto err_;
	}

	/* get spdif clock and init. */
	spdif->hclk = devm_clk_get(&pdev->dev, "spdif_hclk");
	if (IS_ERR(spdif->hclk)) {
		dev_err(&pdev->dev, "Can't retrieve spdif hclock\n");
		spdif->hclk = NULL;
	}
	clk_prepare_enable(spdif->hclk);

	/* get spdif clock and init. */
	spdif->clk = devm_clk_get(&pdev->dev, "spdif_mclk");
	if (IS_ERR(spdif->clk)) {
		dev_err(&pdev->dev, "Can't retrieve spdif clock\n");
		ret = -ENOMEM;
		goto err_;
	}
	clk_set_rate(spdif->clk, 12288000);
	clk_set_rate(spdif->clk, 11289600);
	clk_prepare_enable(spdif->clk);

	spdif->dma_playback.addr = mem_res->start + DATA_OUTBUF;
	spdif->dma_playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
	spdif->dma_playback.maxburst = 4;

	/* set dev name to driver->name for sound card register */
	dev_set_name(&pdev->dev, "%s", pdev->dev.driver->name);

	ret = snd_soc_register_component(&pdev->
		dev, &rockchip_spdif_component,	&rockchip_spdif_dai, 1);
	if (ret) {
		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
		ret = -ENOMEM;
		goto err_;
	}

	ret = rockchip_pcm_platform_register(&pdev->dev);
	if (ret) {
		dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
		goto err_;
	}

	RK_SPDIF_DBG("spdif:spdif probe ok!\n");

	return 0;

err_:
	platform_set_drvdata(pdev, NULL);

	return ret;
}
static int spdif_resume(struct snd_soc_dai *cpu_dai)
{
	RK_SPDIF_DBG("spdif:Entered %s\n", __func__);

	return 0;
}
static int spdif_hw_params(struct snd_pcm_substream *
		substream, struct snd_pcm_hw_params *params,
		struct snd_soc_dai *dai)
{
	struct rockchip_spdif_info *spdif = to_info(dai);
	void __iomem *regs = spdif->regs;
	unsigned long flags;
	int cfgr, dmac, intcr, chnsr_byte[5] = {0};
	int data_type, err_flag, data_len, data_info;
	int bs_num, repetition, burst_info;

	RK_SPDIF_DBG("Entered %s\n", __func__);

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		dai->playback_dma_data = &spdif->dma_playback;
	} else {
		pr_err("spdif:Capture is not supported\n");
		return -EINVAL;
	}

	spin_lock_irqsave(&spdif->lock, flags);

	cfgr = readl(regs + CFGR) & CFGR_VALID_DATA_MASK;

	cfgr &= ~CFGR_VALID_DATA_MASK;
	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S16_LE:
		cfgr |= CFGR_VALID_DATA_16bit;
		break;
	case SNDRV_PCM_FORMAT_S20_3LE:
		cfgr |= CFGR_VALID_DATA_20bit;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		cfgr |= CFGR_VALID_DATA_24bit;
		break;
	default:
		goto err;
	}

	cfgr &= ~CFGR_HALFWORD_TX_MASK;
	cfgr |= CFGR_HALFWORD_TX_ENABLE;

	/* set most MCLK:192kHz */
	cfgr &= ~CFGR_CLK_RATE_MASK;
	cfgr |= (1<<16);

	cfgr &= ~CFGR_JUSTIFIED_MASK;
	cfgr |= CFGR_JUSTIFIED_RIGHT;

	cfgr &= ~CFGR_CSE_MASK;
	cfgr |= CFGR_CSE_DISABLE;

	cfgr &= ~CFGR_LINEAR_MASK;
	cfgr |= CFGR_LINEAR_PCM;

	/* stream type*/
	if (!snd_pcm_format_linear(params_format(params)))
		cfgr |= CFGR_NON_LINEAR_PCM;

	cfgr &= ~CFGR_PRE_CHANGE_MASK;
	cfgr |= CFGR_PRE_CHANGE_ENALBLE;

	writel(cfgr, regs + CFGR);

	intcr = readl(regs + INTCR) & INTCR_SDBEIE_MASK;
	intcr |= INTCR_SDBEIE_ENABLE;
	writel(intcr, regs + INTCR);

	dmac = readl(regs + DMACR) & DMACR_TRAN_DMA_MASK &
		(~DMACR_TRAN_DATA_LEVEL_MASK);
	dmac |= 0x10;
	writel(dmac, regs + DMACR);

	/*
	* channel byte 0:
	* Bit 1
	* 1 Main data field represents linear PCM samples.
	* 0 Main data field used for purposes other purposes.
	*/
	chnsr_byte[0] = (0x0) | (0x0 << 1) |
		(0x0 << 2) | (0x0 << 3) |
		(0x00 << 6);
	chnsr_byte[1] = (0x0);
	chnsr_byte[2] = (0x0) | (0x0 << 4) | (0x0 << 6);
	chnsr_byte[3] = (0x00) | (0x00);
	chnsr_byte[4] = (0x0 << 4) | (0x01 << 1 | 0x0);

	/* set stream type */
	if (!snd_pcm_format_linear(params_format(params))) {
		chnsr_byte[0] |= (0x1<<1);
		chnsr_byte[4] = (0x0<<4)|(0x00<<1|0x0);
	}
	writel((chnsr_byte[4] << 16)
			| (chnsr_byte[4]),
			regs + SPDIF_CHNSR02_ADDR);
	writel((chnsr_byte[3] << 24) | (chnsr_byte[2] << 16) |
		(chnsr_byte[3] << 8) | (chnsr_byte[2]),
		regs + SPDIF_CHNSR01_ADDR);
	writel((chnsr_byte[1] << 24) | (chnsr_byte[0] << 16) |
		(chnsr_byte[1] << 8) | (chnsr_byte[0]),
		regs + SPDIF_CHNSR00_ADDR);

	/* set non-linear params */
	if (!snd_pcm_format_linear(params_format(params))) {
		switch (params_format(params)) {
		case SNDRV_NON_LINEAR_PCM_FORMAT_AC3:
			/* bit0:6 AC-3:0x01, DTS-I -II -III:11,12,13 */
			data_type = burst_info_DATA_TYPE_AC3;
			/*
			* repetition:AC-3:1536
			* DTS-I -II -III:512,1024,2048 EAC3:6144
			*/
			repetition = 1536;
			break;
		case SNDRV_NON_LINEAR_PCM_FORMAT_DTS_I:
			data_type = BURST_INFO_DATA_TYPE_DTS_I;
			repetition = 512;
			break;
		case SNDRV_NON_LINEAR_PCM_FORMAT_EAC3:
			data_type = burst_info_DATA_TYPE_EAC3;
			repetition = 6144;
			break;
		default:
			return -EINVAL;
		}
		err_flag = 0x0;
		data_len = params_period_size(params) * 2 * 16;
		data_info = 0;
		bs_num = 0x0;
		burst_info = (data_len << 16) | (bs_num << 13) |
			(data_info << 8) | (err_flag << 7) | data_type;
		writel(burst_info, regs + SPDIF_BURST_INFO);
		writel(repetition, regs + SPDIF_REPETTION);
	}
	spin_unlock_irqrestore(&spdif->lock, flags);

	return 0;
err:
	spin_unlock_irqrestore(&spdif->lock, flags);
	return -EINVAL;
}