📄 ppidriver.c
字号:
//Sample PPI Driver
//This driver can be used to capture video Frames from the PPI port. The source code can be found in the file linux-2.6.x/drivers/char/adsp-ppifcd.c
//The interface to the camera sensor is shown here.
//Camera Sensor Interface
//Parts of that source are produced here but you will need to refer to the in kernel source for the full driver.
//This driver also has the following features
//" High speed data input from the PPI device via DMA
//" Configuration of the PPI device
//" Interrupts Transfer Complete and Error Detection
//" Fasync notification of Transfer Complete
//The PPI Input device has a number of configurable features, this driver uses the following:
//" POL_S - invert Frame Sync 1 and Frame Sync 2
//" POL_C - invert Clock
//" PPI_DATA_LEN - port width
//" PPI_PACKING - Pack 2 8 bits words into a single 16 bit output
//" CFG_GP_Input_3Syncs - Selects full ( Hsync, Vsync and Frame ) or single Frame Sync
//" GP_Input_Mode - selects input mode
//The PPI Transfer can use a GPIO pin to start the transfer process
//Two Camera Sensor Module Definitions are available as "preset modes" for the driver.
//file: trunk/drivers/char/bfin_ppifcd.h:70-103
#ifdef MT9M001
#define POL_C 0x4000
#define POL_S 0x0000
#define PIXEL_PER_LINE 1280
#define LINES_PER_FRAME 1024
#define CFG_GP_Input_3Syncs 0x0020
#define GP_Input_Mode 0x000C
#define PPI_DATA_LEN DLEN_8
#define PPI_PACKING PACK_EN
#define DMA_FLOW_MODE 0x0000 //STOPMODE
#define DMA_WDSIZE_16 WDSIZE_16
#endif
#ifdef MT9V022
#define POL_C 0x0000
#define POL_S 0x0000
#define PIXEL_PER_LINE 720
#define LINES_PER_FRAME 488
#define CFG_GP_Input_3Syncs 0x0020
#define GP_Input_Mode 0x000C
#define PPI_DATA_LEN DLEN_8
#define PPI_PACKING PACK_EN
#define DMA_FLOW_MODE 0x0000 //STOPMODE
#define DMA_WDSIZE_16 WDSIZE_16
#endif
#endif /* _ADSP_PPIADC_H_ */
//To start understanding this driver first search for the init function.
//Init Function
//Here is the code for the init function
//Note the following features
//" char device with PPI_MAJOR as a major number
//" Uses ppi_fops for a file operations table
//" Device name PPI_DEVNAME
//The major number and the device name are defined at the start of the driver source
//file: trunk/drivers/char/bfin_ppifcd.c:68-74
/************************************************************/
typedef struct PPI_Device_t {
int opened;
int nonblock;
unsigned short irqnum;
}
//file: trunk/drivers/char/bfin_ppifcd.c:ppifcd_init()
int __init ppifcd_init(void)
{
int result;
result = register_chrdev(PPI_MAJOR, PPI_DEVNAME, &ppi_fops);
if (result < 0) {
printk(KERN_WARNING "PPI: can't get minor %d\n", PPI_MAJOR);
return result;
}
printk(KERN_INFO "PPI: ADSP PPI Frame Capture Driver IRQ:%d \n",
IRQ_PPI);
return 0;
}
//File Operations Table
//The file operations table shows the features available to the User code
//Note that
//" Read Only , no Write
//" fasync notification used to send SIGIO to the user code on read completion
//" IOCTL used to configure the driver
//file: trunk/drivers/char/bfin_ppifcd.c:ppi_fops{}
static struct file_operations ppi_fops = {
owner: THIS_MODULE,
read: ppi_read,
ioctl: ppi_ioctl,
open: ppi_open,
release: ppi_release,
fasync: ppi_fasync,
};
//Open Function
//The Open Function sets up the device for use with a user code function.
//Note the following features
//" Minor Number Check
//" Single user check ( only one user process at a time )
//" NON BLOCKING operation available
//" DMA channel requested with ppifcd_irq as an irq handler function
//" PPI Error handler IRQ requested with ppifcd_irq_error as an irq handler function
//" PPI device configured with the function ppifcd_reg_reset
//file: trunk/drivers/char/bfin_ppifcd.c:ppi_open()
static int ppi_open(struct inode *inode, struct file *filp)
{
char intname[20];
int minor = MINOR(inode->i_rdev);
DPRINTK("ppi_open:\n");
/* PPI ? */
if (minor != PPI0_MINOR)
return -ENXIO;
if (ppiinfo.opened)
return -EMFILE;
/* Clear configuration information */
memset(&ppiinfo, 0, sizeof(ppi_device_t));
if (filp->f_flags & O_NONBLOCK)
ppiinfo.nonblock = 1;
ppiinfo.opened = 1;
ppiinfo.done = 0;
ppiinfo.dma_config =
(DMA_FLOW_MODE | WNR | RESTART | DMA_WDSIZE_16 | DMA2D | DI_EN);
ppiinfo.pixel_per_line = PIXEL_PER_LINE;
ppiinfo.lines_per_frame = LINES_PER_FRAME;
ppiinfo.bpp = 8;
ppiinfo.ppi_control =
POL_S | POL_C | PPI_DATA_LEN | PPI_PACKING | CFG_GP_Input_3Syncs |
GP_Input_Mode;
ppiinfo.ppi_status = 0;
ppiinfo.ppi_delay = 0;
ppiinfo.ppi_trigger_gpio = NO_TRIGGER;
ppiinfo.rx_avail = &ppirxq0;
strcpy(intname, PPI_INTNAME);
ppiinfo.irqnum = IRQ_PPI;
filp->private_data = &ppiinfo;
ppifcd_reg_reset(filp->private_data);
/* Request DMA channel, and pass the interrupt handler */
if (request_dma(CH_PPI, "BF533_PPI_DMA") < 0) {
panic("Unable to attach BlackFin PPI DMA channel\n");
return -EFAULT;
} else
set_dma_callback(CH_PPI, (void *)ppifcd_irq,
filp->private_data);
request_irq(IRQ_PPI_ERROR, (void *)ppifcd_irq_error, SA_INTERRUPT,
"PPI ERROR", filp->private_data);
#if (defined(CONFIG_BF537) || defined(CONFIG_BF534) || defined(CONFIG_BF536))
bfin_write_PORTG_FER(0x00FF); /* PPI[7:0] */
bfin_write_PORTF_FER(bfin_read_PORTF_FER() | 0x8300); /* PF.15 PPI_CLK FS1 FS2 */
bfin_write_PORT_MUX(bfin_read_PORT_MUX() & ~0x0E00);
#endif
DPRINTK("ppi_open: return\n");
return 0;
}
//Read Function
//This function services the user request to read data from the device
//Note the following
//" flush out the input memory data cache
//" 2 dimensional DMA input
//" 8 or 16 bit data input words
//" use of gpio flags to trigger PPI
//" this driver is writing directly into the user space buffer
//" this can only be done on a MMUless system
//file: trunk/drivers/char/bfin_ppifcd.c:ppi_read()
static ssize_t ppi_read(struct file *filp, char *buf, size_t count,
loff_t * pos)
{
int ierr;
ppi_device_t *pdev = filp->private_data;
DPRINTK("ppi_read:\n");
if (count <= 0)
return 0;
pdev->done = 0;
/* Invalidate allocated memory in Data Cache */
blackfin_dcache_invalidate_range((u_long) buf, (u_long) (buf + count));
DPRINTK("ppi_read: blackfin_dcache_invalidate_range : DONE\n");
/* configure ppi port for DMA RX */
set_dma_config(CH_PPI, pdev->dma_config);
set_dma_start_addr(CH_PPI, (u_long) buf);
set_dma_x_count(CH_PPI, pdev->pixel_per_line / 2); // Div 2 because of 16-bit packing
set_dma_y_count(CH_PPI, pdev->lines_per_frame);
set_dma_y_modify(CH_PPI, 2);
if (pdev->bpp > 8 || pdev->dma_config & WDSIZE_16)
set_dma_x_modify(CH_PPI, 2);
else
set_dma_x_modify(CH_PPI, 1);
DPRINTK("ppi_read: SETUP DMA : DONE\n");
enable_dma(CH_PPI);
/* Enable PPI */
bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN);
__builtin_bfin_ssync();
if (pdev->ppi_trigger_gpio < NO_TRIGGER) {
bfin_write_FIO_FLAG_S(1 << pdev->ppi_trigger_gpio);
__builtin_bfin_ssync();
bfin_write_FIO_FLAG_C(1 << pdev->ppi_trigger_gpio);
__builtin_bfin_ssync();
}
DPRINTK("ppi_read: PPI ENABLED : DONE\n");
/* Wait for data available */
if (1) {
if (pdev->nonblock)
return -EAGAIN;
else {
DPRINTK("PPI wait_event_interruptible\n");
ierr =
wait_event_interruptible(*(pdev->rx_avail),
pdev->done);
if (ierr) {
/* waiting is broken by a signal */
DPRINTK("PPI wait_event_interruptible ierr\n");
return ierr;
}
}
}
DPRINTK("PPI wait_event_interruptible done\n");
disable_dma(CH_PPI);
DPRINTK("ppi_read: return\n");
return count;
}
//PPI Interrupt Function
//This services the "data transfer complete" interrupt from the DMA controller
//Note the following
//" Sends SIGIO to the user process
//" sets a "read complete" condition
//" wakes up a sleeping reader ( if there is one )
//file: trunk/drivers/char/bfin_ppifcd.c:ppifcd_irq()
static irqreturn_t ppifcd_irq(int irq, void *dev_id, struct pt_regs *regs)
{
ppi_device_t *pdev = (ppi_device_t *) dev_id;
DPRINTK("ppifcd_irq:\n");
/* Acknowledge DMA Interrupt */
clear_dma_irqstat(CH_PPI);
/* disable ppi */
bfin_write_PPI_CONTROL(pdev->ppi_control & ~PORT_EN);
pdev->done = 1;
/* Give a signal to user program. */
if (pdev->fasyc)
kill_fasync(&(pdev->fasyc), SIGIO, POLLIN);
DPRINTK("ppifcd_irq: wake_up_interruptible pdev->done=%d\n",
pdev->done);
/* wake up read */
wake_up_interruptible(pdev->rx_avail);
DPRINTK("ppifcd_irq: return\n");
return IRQ_HANDLED;
}
//PPI Error Interrupt Function
//This services the "data transfer error" interrupt from the DMA controller In this example the Interrupt service function is the same as the data complete function. Normally additional error handling would be added to this driver.
//file: trunk/drivers/char/bfin_ppifcd.c:ppifcd_irq_error()
static irqreturn_t ppifcd_irq_error(int irq, void *dev_id, struct pt_regs *regs)
{
ppi_device_t *pdev = (ppi_device_t *) dev_id;
DPRINTK("ppifcd_error_irq:\n");
DPRINTK("PPI Status = 0x%X\n", bfin_read_PPI_STATUS());
bfin_clear_PPI_STATUS();
/* Acknowledge DMA Interrupt */
clear_dma_irqstat(CH_PPI);
/* disable ppi */
bfin_write_PPI_CONTROL(pdev->ppi_control & ~PORT_EN);
pdev->done = 1;
/* Give a signal to user program. */
if (pdev->fasyc)
kill_fasync(&(pdev->fasyc), SIGIO, POLLIN);
DPRINTK("ppifcd_error_irq: wake_up_interruptible pdev->done=%d\n",
pdev->done);
/* wake up read */
wake_up_interruptible(pdev->rx_avail);
DPRINTK("ppifcd_error_irq: return\n");
return IRQ_HANDLED;
}
//IOCTL Code
//This is used to set up the device for a particular operation. This driver is designed to interface to a Camera. The device can be "preset" for a particular camera if required but the IOCTL functions can modify the preset settings.
//A list of the commands processed is here
//" CMD_PPI_SET_PIXELS_PER_LINE
//" CMD_PPI_SET_LINES_PER_FRAME
//" CMD_PPI_SET_PPICONTROL_REG
//" CMD_PPI_SET_PPIDEALY_REG
//" CMD_SET_TRIGGER_GPIO
//" CMD_PPI_GET_ALLCONFIG
//" CMD_PPI_GET_SYSTEMCLOCK
//file: trunk/drivers/char/bfin_ppifcd.c:ppi_ioctl()
static int ppi_ioctl(struct inode *inode, struct file *filp, uint cmd,
unsigned long arg)
{
u_long value;
ppi_device_t *pdev = filp->private_data;
switch (cmd) {
case CMD_PPI_SET_PIXELS_PER_LINE:
{
DPRINTK("ppi_ioctl: CMD_PPI_SET_PIXELS_PER_LINE\n");
pdev->pixel_per_line = (unsigned short)arg;
bfin_write_PPI_COUNT(pdev->pixel_per_line - 1);
break;
}
case CMD_PPI_SET_LINES_PER_FRAME:
{
DPRINTK("ppi_ioctl: CMD_PPI_SET_LINES_PER_FRAME\n");
pdev->lines_per_frame = (unsigned short)arg;
bfin_write_PPI_FRAME(pdev->lines_per_frame);
break;
}
case CMD_PPI_SET_PPICONTROL_REG:
{
DPRINTK("ppi_ioctl: CMD_PPI_SET_PPICONTROL_REG\n");
pdev->ppi_control = ((unsigned short)arg) & ~PORT_EN;
bfin_write_PPI_CONTROL(pdev->ppi_control);
break;
}
case CMD_PPI_SET_PPIDEALY_REG:
{
DPRINTK("ppi_ioctl: CMD_PPI_SET_PPIDEALY_REG\n");
pdev->ppi_delay = (unsigned short)arg;
bfin_write_PPI_DELAY(pdev->ppi_delay);
break;
}
case CMD_SET_TRIGGER_GPIO:
{
DPRINTK("ppi_ioctl: CMD_SET_TRIGGER_GPIO\n");
pdev->ppi_trigger_gpio = (unsigned short)arg;
break;
}
case CMD_PPI_GET_ALLCONFIG:
{
break;
}
case CMD_PPI_GET_SYSTEMCLOCK:
{
value = get_sclk();
DPRINTK
("ppi_ioctl: CMD_PPI_GET_SYSTEMCLOCK SCLK: %d \n",
(int)value);
copy_to_user((unsigned long *)arg, &value,
sizeof(unsigned long));
break;
}
default:
return -EINVAL;
}
return 0;
}
//FASYNC Helper
//This is called when the user code sets up the device to deliver SIGIO signals.
//It is needed by the inner kernel to associate the fasync wait queue with the user process.
//file: trunk/drivers/char/bfin_ppifcd.c:ppi_fasync()
static int ppi_fasync(int fd, struct file *filp, int on)
{
ppi_device_t *pdev = filp->private_data;
return fasync_helper(fd, filp, on, &(pdev->fasyc));
}
//Helper functions
//This driver has a few helper functions to perform the following
//" get the system clock
//" reset the ppi device
//ppi_get_sclk
//file: trunk/drivers/char/bfin_ppifcd.c:ppi_get_sclk()
//cound not find function ppi_get_sclk
//ppifcd_reg_reset
//file: trunk/drivers/char/bfin_ppifcd.c:ppifcd_reg_reset()
void ppifcd_reg_reset(ppi_device_t *pdev)
{
/* Do some initializaion stuff here based on the defined Camera Module
so we don't have to use ioctls */
bfin_clear_PPI_STATUS();
bfin_write_PPI_CONTROL(pdev->ppi_control & ~PORT_EN);
bfin_write_PPI_DELAY(pdev->ppi_delay);
bfin_write_PPI_COUNT(pdev->pixel_per_line - 1);
bfin_write_PPI_FRAME(pdev->lines_per_frame);
return;
}
//Release Function
//The Release function terminates the use of the device by the user function
//NOTE PPI error IRQ is not released yet
//file: trunk/drivers/char/bfin_ppifcd.c:ppi_release()
static int ppi_release(struct inode *inode, struct file *filp)
{
ppi_device_t *pdev = filp->private_data;
DPRINTK("ppi_release: close()\n");
/* After finish DMA, release it. */
free_dma(CH_PPI);
ppifcd_reg_reset(pdev);
pdev->opened = 0;
ppi_fasync(-1, filp, 0);
DPRINTK("ppi_release: close() return\n");
return 0;
}
//Driver Removal
//If the Driver is a module and not permanently compiled into the Kernel the following code will unregister the device and clean up the system.
//file: trunk/drivers/char/bfin_ppifcd.c:ppifcd_uninit()
void __exit ppifcd_uninit(void)
{
unregister_chrdev(PPI_MAJOR, PPI_DEVNAME);
printk(KERN_ALERT "Goodbye PPI\n");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -