/*
* File: bf5xx_sport.c
* Based on:
* Author: Roy Huang <roy.huang@analog.com>
*
* Created: Tue Sep 21 10:52:42 CEST 2004
* Description:
* Blackfin SPORT Driver
*
* Copyright 2004-2007 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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, see the file COPYING, or write
* to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/gpio.h>
#include <linux/bug.h>
#include <asm/portmux.h>
#include <asm/dma.h>
#include <asm/blackfin.h>
#include <asm/cacheflush.h>
#include "bf5xx-sport.h"
/* delay between frame sync pulse and first data bit in multichannel mode */
#define FRAME_DELAY (1<<12)
struct sport_device *sport_handle;
EXPORT_SYMBOL(sport_handle);
/* note: multichannel is in units of 8 channels,
* tdm_count is # channels NOT / 8 ! */
int sport_set_multichannel(struct sport_device *sport,
int tdm_count, u32 mask, int packed)
{
pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__,
tdm_count, mask, packed);
if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
return -EBUSY;
if (tdm_count & 0x7)
return -EINVAL;
if (tdm_count > 32)
return -EINVAL; /* Only support less than 32 channels now */
if (tdm_count) {
sport->regs->mcmc1 = ((tdm_count>>3)-1) << 12;
sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \
(packed ? (MCDTXPE|MCDRXPE) : 0);
sport->regs->mtcs0 = mask;
sport->regs->mrcs0 = mask;
sport->regs->mtcs1 = 0;
sport->regs->mrcs1 = 0;
sport->regs->mtcs2 = 0;
sport->regs->mrcs2 = 0;
sport->regs->mtcs3 = 0;
sport->regs->mrcs3 = 0;
} else {
sport->regs->mcmc1 = 0;
sport->regs->mcmc2 = 0;
sport->regs->mtcs0 = 0;
sport->regs->mrcs0 = 0;
}
sport->regs->mtcs1 = 0; sport->regs->mtcs2 = 0; sport->regs->mtcs3 = 0;
sport->regs->mrcs1 = 0; sport->regs->mrcs2 = 0; sport->regs->mrcs3 = 0;
SSYNC();
return 0;
}
EXPORT_SYMBOL(sport_set_multichannel);
int sport_config_rx(struct sport_device *sport, unsigned int rcr1,
unsigned int rcr2, unsigned int clkdiv, unsigned int fsdiv)
{
if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
return -EBUSY;
sport->regs->rcr1 = rcr1;
sport->regs->rcr2 = rcr2;
sport->regs->rclkdiv = clkdiv;
sport->regs->rfsdiv = fsdiv;
SSYNC();
return 0;
}
EXPORT_SYMBOL(sport_config_rx);
int sport_config_tx(struct sport_device *sport, unsigned int tcr1,
unsigned int tcr2, unsigned int clkdiv, unsigned int fsdiv)
{
if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
return -EBUSY;
sport->regs->tcr1 = tcr1;
sport->regs->tcr2 = tcr2;
sport->regs->tclkdiv = clkdiv;
sport->regs->tfsdiv = fsdiv;
SSYNC();
return 0;
}
EXPORT_SYMBOL(sport_config_tx);
static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
size_t fragsize, unsigned int cfg,
unsigned int x_count, unsigned int ycount, size_t wdsize)
{
int i;
for (