162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe" 462306a36Sopenharmony_ci * multifunction chip. Currently works with the Omnivision OV7670 562306a36Sopenharmony_ci * sensor. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * The data sheet for this device can be found at: 862306a36Sopenharmony_ci * http://wiki.laptop.org/images/5/5c/88ALP01_Datasheet_July_2007.pdf 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright 2006-11 One Laptop Per Child Association, Inc. 1162306a36Sopenharmony_ci * Copyright 2006-11 Jonathan Corbet <corbet@lwn.net> 1262306a36Sopenharmony_ci * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk> 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Written by Jonathan Corbet, corbet@lwn.net. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * v4l2_device/v4l2_subdev conversion by: 1762306a36Sopenharmony_ci * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl> 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/pci.h> 2362306a36Sopenharmony_ci#include <linux/i2c.h> 2462306a36Sopenharmony_ci#include <linux/interrupt.h> 2562306a36Sopenharmony_ci#include <linux/spinlock.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/videodev2.h> 2862306a36Sopenharmony_ci#include <media/v4l2-device.h> 2962306a36Sopenharmony_ci#include <media/i2c/ov7670.h> 3062306a36Sopenharmony_ci#include <linux/device.h> 3162306a36Sopenharmony_ci#include <linux/wait.h> 3262306a36Sopenharmony_ci#include <linux/delay.h> 3362306a36Sopenharmony_ci#include <linux/io.h> 3462306a36Sopenharmony_ci#include <linux/clkdev.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "mcam-core.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define CAFE_VERSION 0x000002 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * Parameters. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); 4562306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver"); 4662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct cafe_camera { 4962306a36Sopenharmony_ci int registered; /* Fully initialized? */ 5062306a36Sopenharmony_ci struct mcam_camera mcam; 5162306a36Sopenharmony_ci struct pci_dev *pdev; 5262306a36Sopenharmony_ci struct i2c_adapter *i2c_adapter; 5362306a36Sopenharmony_ci wait_queue_head_t smbus_wait; /* Waiting on i2c events */ 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Most of the camera controller registers are defined in mcam-core.h, 5862306a36Sopenharmony_ci * but the Cafe platform has some additional registers of its own; 5962306a36Sopenharmony_ci * they are described here. 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * "General purpose register" has a couple of GPIOs used for sensor 6462306a36Sopenharmony_ci * power and reset on OLPC XO 1.0 systems. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci#define REG_GPR 0xb4 6762306a36Sopenharmony_ci#define GPR_C1EN 0x00000020 /* Pad 1 (power down) enable */ 6862306a36Sopenharmony_ci#define GPR_C0EN 0x00000010 /* Pad 0 (reset) enable */ 6962306a36Sopenharmony_ci#define GPR_C1 0x00000002 /* Control 1 value */ 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Control 0 is wired to reset on OLPC machines. For ov7x sensors, 7262306a36Sopenharmony_ci * it is active low. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci#define GPR_C0 0x00000001 /* Control 0 value */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * These registers control the SMBUS module for communicating 7862306a36Sopenharmony_ci * with the sensor. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci#define REG_TWSIC0 0xb8 /* TWSI (smbus) control 0 */ 8162306a36Sopenharmony_ci#define TWSIC0_EN 0x00000001 /* TWSI enable */ 8262306a36Sopenharmony_ci#define TWSIC0_MODE 0x00000002 /* 1 = 16-bit, 0 = 8-bit */ 8362306a36Sopenharmony_ci#define TWSIC0_SID 0x000003fc /* Slave ID */ 8462306a36Sopenharmony_ci/* 8562306a36Sopenharmony_ci * Subtle trickery: the slave ID field starts with bit 2. But the 8662306a36Sopenharmony_ci * Linux i2c stack wants to treat the bottommost bit as a separate 8762306a36Sopenharmony_ci * read/write bit, which is why slave ID's are usually presented 8862306a36Sopenharmony_ci * >>1. For consistency with that behavior, we shift over three 8962306a36Sopenharmony_ci * bits instead of two. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci#define TWSIC0_SID_SHIFT 3 9262306a36Sopenharmony_ci#define TWSIC0_CLKDIV 0x0007fc00 /* Clock divider */ 9362306a36Sopenharmony_ci#define TWSIC0_MASKACK 0x00400000 /* Mask ack from sensor */ 9462306a36Sopenharmony_ci#define TWSIC0_OVMAGIC 0x00800000 /* Make it work on OV sensors */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define REG_TWSIC1 0xbc /* TWSI control 1 */ 9762306a36Sopenharmony_ci#define TWSIC1_DATA 0x0000ffff /* Data to/from camchip */ 9862306a36Sopenharmony_ci#define TWSIC1_ADDR 0x00ff0000 /* Address (register) */ 9962306a36Sopenharmony_ci#define TWSIC1_ADDR_SHIFT 16 10062306a36Sopenharmony_ci#define TWSIC1_READ 0x01000000 /* Set for read op */ 10162306a36Sopenharmony_ci#define TWSIC1_WSTAT 0x02000000 /* Write status */ 10262306a36Sopenharmony_ci#define TWSIC1_RVALID 0x04000000 /* Read data valid */ 10362306a36Sopenharmony_ci#define TWSIC1_ERROR 0x08000000 /* Something screwed up */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* 10662306a36Sopenharmony_ci * Here's the weird global control registers 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci#define REG_GL_CSR 0x3004 /* Control/status register */ 10962306a36Sopenharmony_ci#define GCSR_SRS 0x00000001 /* SW Reset set */ 11062306a36Sopenharmony_ci#define GCSR_SRC 0x00000002 /* SW Reset clear */ 11162306a36Sopenharmony_ci#define GCSR_MRS 0x00000004 /* Master reset set */ 11262306a36Sopenharmony_ci#define GCSR_MRC 0x00000008 /* HW Reset clear */ 11362306a36Sopenharmony_ci#define GCSR_CCIC_EN 0x00004000 /* CCIC Clock enable */ 11462306a36Sopenharmony_ci#define REG_GL_IMASK 0x300c /* Interrupt mask register */ 11562306a36Sopenharmony_ci#define GIMSK_CCIC_EN 0x00000004 /* CCIC Interrupt enable */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define REG_GL_FCR 0x3038 /* GPIO functional control register */ 11862306a36Sopenharmony_ci#define GFCR_GPIO_ON 0x08 /* Camera GPIO enabled */ 11962306a36Sopenharmony_ci#define REG_GL_GPIOR 0x315c /* GPIO register */ 12062306a36Sopenharmony_ci#define GGPIO_OUT 0x80000 /* GPIO output */ 12162306a36Sopenharmony_ci#define GGPIO_VAL 0x00008 /* Output pin value */ 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define REG_LEN (REG_GL_IMASK + 4) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * Debugging and related. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci#define cam_err(cam, fmt, arg...) \ 13062306a36Sopenharmony_ci dev_err(&(cam)->pdev->dev, fmt, ##arg); 13162306a36Sopenharmony_ci#define cam_warn(cam, fmt, arg...) \ 13262306a36Sopenharmony_ci dev_warn(&(cam)->pdev->dev, fmt, ##arg); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* -------------------------------------------------------------------- */ 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * The I2C/SMBUS interface to the camera itself starts here. The 13762306a36Sopenharmony_ci * controller handles SMBUS itself, presenting a relatively simple register 13862306a36Sopenharmony_ci * interface; all we have to do is to tell it where to route the data. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci#define CAFE_SMBUS_TIMEOUT (HZ) /* generous */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int cafe_smbus_write_done(struct mcam_camera *mcam) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci unsigned long flags; 14562306a36Sopenharmony_ci int c1; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* 14862306a36Sopenharmony_ci * We must delay after the interrupt, or the controller gets confused 14962306a36Sopenharmony_ci * and never does give us good status. Fortunately, we don't do this 15062306a36Sopenharmony_ci * often. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci udelay(20); 15362306a36Sopenharmony_ci spin_lock_irqsave(&mcam->dev_lock, flags); 15462306a36Sopenharmony_ci c1 = mcam_reg_read(mcam, REG_TWSIC1); 15562306a36Sopenharmony_ci spin_unlock_irqrestore(&mcam->dev_lock, flags); 15662306a36Sopenharmony_ci return (c1 & (TWSIC1_WSTAT|TWSIC1_ERROR)) != TWSIC1_WSTAT; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int cafe_smbus_write_data(struct cafe_camera *cam, 16062306a36Sopenharmony_ci u16 addr, u8 command, u8 value) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci unsigned int rval; 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci struct mcam_camera *mcam = &cam->mcam; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci spin_lock_irqsave(&mcam->dev_lock, flags); 16762306a36Sopenharmony_ci rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID); 16862306a36Sopenharmony_ci rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */ 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * Marvell sez set clkdiv to all 1's for now. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci rval |= TWSIC0_CLKDIV; 17362306a36Sopenharmony_ci mcam_reg_write(mcam, REG_TWSIC0, rval); 17462306a36Sopenharmony_ci (void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */ 17562306a36Sopenharmony_ci rval = value | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR); 17662306a36Sopenharmony_ci mcam_reg_write(mcam, REG_TWSIC1, rval); 17762306a36Sopenharmony_ci spin_unlock_irqrestore(&mcam->dev_lock, flags); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Unfortunately, reading TWSIC1 too soon after sending a command 18062306a36Sopenharmony_ci * causes the device to die. 18162306a36Sopenharmony_ci * Use a busy-wait because we often send a large quantity of small 18262306a36Sopenharmony_ci * commands at-once; using msleep() would cause a lot of context 18362306a36Sopenharmony_ci * switches which take longer than 2ms, resulting in a noticeable 18462306a36Sopenharmony_ci * boot-time and capture-start delays. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci mdelay(2); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * Another sad fact is that sometimes, commands silently complete but 19062306a36Sopenharmony_ci * cafe_smbus_write_done() never becomes aware of this. 19162306a36Sopenharmony_ci * This happens at random and appears to possible occur with any 19262306a36Sopenharmony_ci * command. 19362306a36Sopenharmony_ci * We don't understand why this is. We work around this issue 19462306a36Sopenharmony_ci * with the timeout in the wait below, assuming that all commands 19562306a36Sopenharmony_ci * complete within the timeout. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci wait_event_timeout(cam->smbus_wait, cafe_smbus_write_done(mcam), 19862306a36Sopenharmony_ci CAFE_SMBUS_TIMEOUT); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci spin_lock_irqsave(&mcam->dev_lock, flags); 20162306a36Sopenharmony_ci rval = mcam_reg_read(mcam, REG_TWSIC1); 20262306a36Sopenharmony_ci spin_unlock_irqrestore(&mcam->dev_lock, flags); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (rval & TWSIC1_WSTAT) { 20562306a36Sopenharmony_ci cam_err(cam, "SMBUS write (%02x/%02x/%02x) timed out\n", addr, 20662306a36Sopenharmony_ci command, value); 20762306a36Sopenharmony_ci return -EIO; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci if (rval & TWSIC1_ERROR) { 21062306a36Sopenharmony_ci cam_err(cam, "SMBUS write (%02x/%02x/%02x) error\n", addr, 21162306a36Sopenharmony_ci command, value); 21262306a36Sopenharmony_ci return -EIO; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int cafe_smbus_read_done(struct mcam_camera *mcam) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci unsigned long flags; 22262306a36Sopenharmony_ci int c1; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * We must delay after the interrupt, or the controller gets confused 22662306a36Sopenharmony_ci * and never does give us good status. Fortunately, we don't do this 22762306a36Sopenharmony_ci * often. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci udelay(20); 23062306a36Sopenharmony_ci spin_lock_irqsave(&mcam->dev_lock, flags); 23162306a36Sopenharmony_ci c1 = mcam_reg_read(mcam, REG_TWSIC1); 23262306a36Sopenharmony_ci spin_unlock_irqrestore(&mcam->dev_lock, flags); 23362306a36Sopenharmony_ci return c1 & (TWSIC1_RVALID|TWSIC1_ERROR); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int cafe_smbus_read_data(struct cafe_camera *cam, 23962306a36Sopenharmony_ci u16 addr, u8 command, u8 *value) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci unsigned int rval; 24262306a36Sopenharmony_ci unsigned long flags; 24362306a36Sopenharmony_ci struct mcam_camera *mcam = &cam->mcam; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci spin_lock_irqsave(&mcam->dev_lock, flags); 24662306a36Sopenharmony_ci rval = TWSIC0_EN | ((addr << TWSIC0_SID_SHIFT) & TWSIC0_SID); 24762306a36Sopenharmony_ci rval |= TWSIC0_OVMAGIC; /* Make OV sensors work */ 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * Marvel sez set clkdiv to all 1's for now. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci rval |= TWSIC0_CLKDIV; 25262306a36Sopenharmony_ci mcam_reg_write(mcam, REG_TWSIC0, rval); 25362306a36Sopenharmony_ci (void) mcam_reg_read(mcam, REG_TWSIC1); /* force write */ 25462306a36Sopenharmony_ci rval = TWSIC1_READ | ((command << TWSIC1_ADDR_SHIFT) & TWSIC1_ADDR); 25562306a36Sopenharmony_ci mcam_reg_write(mcam, REG_TWSIC1, rval); 25662306a36Sopenharmony_ci spin_unlock_irqrestore(&mcam->dev_lock, flags); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci wait_event_timeout(cam->smbus_wait, 25962306a36Sopenharmony_ci cafe_smbus_read_done(mcam), CAFE_SMBUS_TIMEOUT); 26062306a36Sopenharmony_ci spin_lock_irqsave(&mcam->dev_lock, flags); 26162306a36Sopenharmony_ci rval = mcam_reg_read(mcam, REG_TWSIC1); 26262306a36Sopenharmony_ci spin_unlock_irqrestore(&mcam->dev_lock, flags); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (rval & TWSIC1_ERROR) { 26562306a36Sopenharmony_ci cam_err(cam, "SMBUS read (%02x/%02x) error\n", addr, command); 26662306a36Sopenharmony_ci return -EIO; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci if (!(rval & TWSIC1_RVALID)) { 26962306a36Sopenharmony_ci cam_err(cam, "SMBUS read (%02x/%02x) timed out\n", addr, 27062306a36Sopenharmony_ci command); 27162306a36Sopenharmony_ci return -EIO; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci *value = rval & 0xff; 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci/* 27862306a36Sopenharmony_ci * Perform a transfer over SMBUS. This thing is called under 27962306a36Sopenharmony_ci * the i2c bus lock, so we shouldn't race with ourselves... 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistatic int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr, 28262306a36Sopenharmony_ci unsigned short flags, char rw, u8 command, 28362306a36Sopenharmony_ci int size, union i2c_smbus_data *data) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct cafe_camera *cam = i2c_get_adapdata(adapter); 28662306a36Sopenharmony_ci int ret = -EINVAL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci * This interface would appear to only do byte data ops. OK 29062306a36Sopenharmony_ci * it can do word too, but the cam chip has no use for that. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ci if (size != I2C_SMBUS_BYTE_DATA) { 29362306a36Sopenharmony_ci cam_err(cam, "funky xfer size %d\n", size); 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (rw == I2C_SMBUS_WRITE) 29862306a36Sopenharmony_ci ret = cafe_smbus_write_data(cam, addr, command, data->byte); 29962306a36Sopenharmony_ci else if (rw == I2C_SMBUS_READ) 30062306a36Sopenharmony_ci ret = cafe_smbus_read_data(cam, addr, command, &data->byte); 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic void cafe_smbus_enable_irq(struct cafe_camera *cam) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci unsigned long flags; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci spin_lock_irqsave(&cam->mcam.dev_lock, flags); 31062306a36Sopenharmony_ci mcam_reg_set_bit(&cam->mcam, REG_IRQMASK, TWSIIRQS); 31162306a36Sopenharmony_ci spin_unlock_irqrestore(&cam->mcam.dev_lock, flags); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic u32 cafe_smbus_func(struct i2c_adapter *adapter) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci return I2C_FUNC_SMBUS_READ_BYTE_DATA | 31762306a36Sopenharmony_ci I2C_FUNC_SMBUS_WRITE_BYTE_DATA; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic const struct i2c_algorithm cafe_smbus_algo = { 32162306a36Sopenharmony_ci .smbus_xfer = cafe_smbus_xfer, 32262306a36Sopenharmony_ci .functionality = cafe_smbus_func 32362306a36Sopenharmony_ci}; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int cafe_smbus_setup(struct cafe_camera *cam) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct i2c_adapter *adap; 32862306a36Sopenharmony_ci int ret; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci adap = kzalloc(sizeof(*adap), GFP_KERNEL); 33162306a36Sopenharmony_ci if (adap == NULL) 33262306a36Sopenharmony_ci return -ENOMEM; 33362306a36Sopenharmony_ci adap->owner = THIS_MODULE; 33462306a36Sopenharmony_ci adap->algo = &cafe_smbus_algo; 33562306a36Sopenharmony_ci strscpy(adap->name, "cafe_ccic", sizeof(adap->name)); 33662306a36Sopenharmony_ci adap->dev.parent = &cam->pdev->dev; 33762306a36Sopenharmony_ci i2c_set_adapdata(adap, cam); 33862306a36Sopenharmony_ci ret = i2c_add_adapter(adap); 33962306a36Sopenharmony_ci if (ret) { 34062306a36Sopenharmony_ci printk(KERN_ERR "Unable to register cafe i2c adapter\n"); 34162306a36Sopenharmony_ci kfree(adap); 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci cam->i2c_adapter = adap; 34662306a36Sopenharmony_ci cafe_smbus_enable_irq(cam); 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void cafe_smbus_shutdown(struct cafe_camera *cam) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci i2c_del_adapter(cam->i2c_adapter); 35362306a36Sopenharmony_ci kfree(cam->i2c_adapter); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* 35862306a36Sopenharmony_ci * Controller-level stuff 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void cafe_ctlr_init(struct mcam_camera *mcam) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci unsigned long flags; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci spin_lock_irqsave(&mcam->dev_lock, flags); 36662306a36Sopenharmony_ci /* 36762306a36Sopenharmony_ci * Added magic to bring up the hardware on the B-Test board 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_ci mcam_reg_write(mcam, 0x3038, 0x8); 37062306a36Sopenharmony_ci mcam_reg_write(mcam, 0x315c, 0x80008); 37162306a36Sopenharmony_ci /* 37262306a36Sopenharmony_ci * Go through the dance needed to wake the device up. 37362306a36Sopenharmony_ci * Note that these registers are global and shared 37462306a36Sopenharmony_ci * with the NAND and SD devices. Interaction between the 37562306a36Sopenharmony_ci * three still needs to be examined. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */ 37862306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRC); 37962306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GL_CSR, GCSR_SRC|GCSR_MRS); 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * Here we must wait a bit for the controller to come around. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci spin_unlock_irqrestore(&mcam->dev_lock, flags); 38462306a36Sopenharmony_ci msleep(5); 38562306a36Sopenharmony_ci spin_lock_irqsave(&mcam->dev_lock, flags); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC); 38862306a36Sopenharmony_ci mcam_reg_set_bit(mcam, REG_GL_IMASK, GIMSK_CCIC_EN); 38962306a36Sopenharmony_ci /* 39062306a36Sopenharmony_ci * Mask all interrupts. 39162306a36Sopenharmony_ci */ 39262306a36Sopenharmony_ci mcam_reg_write(mcam, REG_IRQMASK, 0); 39362306a36Sopenharmony_ci spin_unlock_irqrestore(&mcam->dev_lock, flags); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int cafe_ctlr_power_up(struct mcam_camera *mcam) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci /* 40062306a36Sopenharmony_ci * Part one of the sensor dance: turn the global 40162306a36Sopenharmony_ci * GPIO signal on. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON); 40462306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT|GGPIO_VAL); 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * Put the sensor into operational mode (assumes OLPC-style 40762306a36Sopenharmony_ci * wiring). Control 0 is reset - set to 1 to operate. 40862306a36Sopenharmony_ci * Control 1 is power down, set to 0 to operate. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ 41162306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return 0; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic void cafe_ctlr_power_down(struct mcam_camera *mcam) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C1); 41962306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GL_FCR, GFCR_GPIO_ON); 42062306a36Sopenharmony_ci mcam_reg_write(mcam, REG_GL_GPIOR, GGPIO_OUT); 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci/* 42662306a36Sopenharmony_ci * The platform interrupt handler. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_cistatic irqreturn_t cafe_irq(int irq, void *data) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct cafe_camera *cam = data; 43162306a36Sopenharmony_ci struct mcam_camera *mcam = &cam->mcam; 43262306a36Sopenharmony_ci unsigned int irqs, handled; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci spin_lock(&mcam->dev_lock); 43562306a36Sopenharmony_ci irqs = mcam_reg_read(mcam, REG_IRQSTAT); 43662306a36Sopenharmony_ci handled = cam->registered && mccic_irq(mcam, irqs); 43762306a36Sopenharmony_ci if (irqs & TWSIIRQS) { 43862306a36Sopenharmony_ci mcam_reg_write(mcam, REG_IRQSTAT, TWSIIRQS); 43962306a36Sopenharmony_ci wake_up(&cam->smbus_wait); 44062306a36Sopenharmony_ci handled = 1; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci spin_unlock(&mcam->dev_lock); 44362306a36Sopenharmony_ci return IRQ_RETVAL(handled); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic struct ov7670_config sensor_cfg = { 44962306a36Sopenharmony_ci /* 45062306a36Sopenharmony_ci * Exclude QCIF mode, because it only captures a tiny portion 45162306a36Sopenharmony_ci * of the sensor FOV 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci .min_width = 320, 45462306a36Sopenharmony_ci .min_height = 240, 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * Set the clock speed for the XO 1; I don't believe this 45862306a36Sopenharmony_ci * driver has ever run anywhere else. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ci .clock_speed = 45, 46162306a36Sopenharmony_ci .use_smbus = 1, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic struct i2c_board_info ov7670_info = { 46562306a36Sopenharmony_ci .type = "ov7670", 46662306a36Sopenharmony_ci .addr = 0x42 >> 1, 46762306a36Sopenharmony_ci .platform_data = &sensor_cfg, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/* -------------------------------------------------------------------------- */ 47162306a36Sopenharmony_ci/* 47262306a36Sopenharmony_ci * PCI interface stuff. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int cafe_pci_probe(struct pci_dev *pdev, 47662306a36Sopenharmony_ci const struct pci_device_id *id) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci int ret; 47962306a36Sopenharmony_ci struct cafe_camera *cam; 48062306a36Sopenharmony_ci struct mcam_camera *mcam; 48162306a36Sopenharmony_ci struct v4l2_async_connection *asd; 48262306a36Sopenharmony_ci struct i2c_client *i2c_dev; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* 48562306a36Sopenharmony_ci * Start putting together one of our big camera structures. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci ret = -ENOMEM; 48862306a36Sopenharmony_ci cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); 48962306a36Sopenharmony_ci if (cam == NULL) 49062306a36Sopenharmony_ci goto out; 49162306a36Sopenharmony_ci pci_set_drvdata(pdev, cam); 49262306a36Sopenharmony_ci cam->pdev = pdev; 49362306a36Sopenharmony_ci mcam = &cam->mcam; 49462306a36Sopenharmony_ci mcam->chip_id = MCAM_CAFE; 49562306a36Sopenharmony_ci spin_lock_init(&mcam->dev_lock); 49662306a36Sopenharmony_ci init_waitqueue_head(&cam->smbus_wait); 49762306a36Sopenharmony_ci mcam->plat_power_up = cafe_ctlr_power_up; 49862306a36Sopenharmony_ci mcam->plat_power_down = cafe_ctlr_power_down; 49962306a36Sopenharmony_ci mcam->dev = &pdev->dev; 50062306a36Sopenharmony_ci /* 50162306a36Sopenharmony_ci * Vmalloc mode for buffers is traditional with this driver. 50262306a36Sopenharmony_ci * We *might* be able to run DMA_contig, especially on a system 50362306a36Sopenharmony_ci * with CMA in it. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_ci mcam->buffer_mode = B_vmalloc; 50662306a36Sopenharmony_ci /* 50762306a36Sopenharmony_ci * Get set up on the PCI bus. 50862306a36Sopenharmony_ci */ 50962306a36Sopenharmony_ci ret = pci_enable_device(pdev); 51062306a36Sopenharmony_ci if (ret) 51162306a36Sopenharmony_ci goto out_free; 51262306a36Sopenharmony_ci pci_set_master(pdev); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci ret = -EIO; 51562306a36Sopenharmony_ci mcam->regs = pci_iomap(pdev, 0, 0); 51662306a36Sopenharmony_ci if (!mcam->regs) { 51762306a36Sopenharmony_ci printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n"); 51862306a36Sopenharmony_ci goto out_disable; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci mcam->regs_size = pci_resource_len(pdev, 0); 52162306a36Sopenharmony_ci ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam); 52262306a36Sopenharmony_ci if (ret) 52362306a36Sopenharmony_ci goto out_iounmap; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* 52662306a36Sopenharmony_ci * Initialize the controller. 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci cafe_ctlr_init(mcam); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * Set up I2C/SMBUS communications. We have to drop the mutex here 53262306a36Sopenharmony_ci * because the sensor could attach in this call chain, leading to 53362306a36Sopenharmony_ci * unsightly deadlocks. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci ret = cafe_smbus_setup(cam); 53662306a36Sopenharmony_ci if (ret) 53762306a36Sopenharmony_ci goto out_pdown; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = v4l2_device_register(mcam->dev, &mcam->v4l2_dev); 54062306a36Sopenharmony_ci if (ret) 54162306a36Sopenharmony_ci goto out_smbus_shutdown; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci v4l2_async_nf_init(&mcam->notifier, &mcam->v4l2_dev); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci asd = v4l2_async_nf_add_i2c(&mcam->notifier, 54662306a36Sopenharmony_ci i2c_adapter_id(cam->i2c_adapter), 54762306a36Sopenharmony_ci ov7670_info.addr, 54862306a36Sopenharmony_ci struct v4l2_async_connection); 54962306a36Sopenharmony_ci if (IS_ERR(asd)) { 55062306a36Sopenharmony_ci ret = PTR_ERR(asd); 55162306a36Sopenharmony_ci goto out_v4l2_device_unregister; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ret = mccic_register(mcam); 55562306a36Sopenharmony_ci if (ret) 55662306a36Sopenharmony_ci goto out_v4l2_device_unregister; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci clkdev_create(mcam->mclk, "xclk", "%d-%04x", 55962306a36Sopenharmony_ci i2c_adapter_id(cam->i2c_adapter), ov7670_info.addr); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci i2c_dev = i2c_new_client_device(cam->i2c_adapter, &ov7670_info); 56262306a36Sopenharmony_ci if (IS_ERR(i2c_dev)) { 56362306a36Sopenharmony_ci ret = PTR_ERR(i2c_dev); 56462306a36Sopenharmony_ci goto out_mccic_shutdown; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci cam->registered = 1; 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ciout_mccic_shutdown: 57162306a36Sopenharmony_ci mccic_shutdown(mcam); 57262306a36Sopenharmony_ciout_v4l2_device_unregister: 57362306a36Sopenharmony_ci v4l2_device_unregister(&mcam->v4l2_dev); 57462306a36Sopenharmony_ciout_smbus_shutdown: 57562306a36Sopenharmony_ci cafe_smbus_shutdown(cam); 57662306a36Sopenharmony_ciout_pdown: 57762306a36Sopenharmony_ci cafe_ctlr_power_down(mcam); 57862306a36Sopenharmony_ci free_irq(pdev->irq, cam); 57962306a36Sopenharmony_ciout_iounmap: 58062306a36Sopenharmony_ci pci_iounmap(pdev, mcam->regs); 58162306a36Sopenharmony_ciout_disable: 58262306a36Sopenharmony_ci pci_disable_device(pdev); 58362306a36Sopenharmony_ciout_free: 58462306a36Sopenharmony_ci kfree(cam); 58562306a36Sopenharmony_ciout: 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* 59162306a36Sopenharmony_ci * Shut down an initialized device 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_cistatic void cafe_shutdown(struct cafe_camera *cam) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci mccic_shutdown(&cam->mcam); 59662306a36Sopenharmony_ci v4l2_device_unregister(&cam->mcam.v4l2_dev); 59762306a36Sopenharmony_ci cafe_smbus_shutdown(cam); 59862306a36Sopenharmony_ci free_irq(cam->pdev->irq, cam); 59962306a36Sopenharmony_ci pci_iounmap(cam->pdev, cam->mcam.regs); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic void cafe_pci_remove(struct pci_dev *pdev) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci struct cafe_camera *cam = pci_get_drvdata(pdev); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (cam == NULL) { 60862306a36Sopenharmony_ci printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev); 60962306a36Sopenharmony_ci return; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci cafe_shutdown(cam); 61262306a36Sopenharmony_ci kfree(cam); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci/* 61762306a36Sopenharmony_ci * Basic power management. 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_cistatic int __maybe_unused cafe_pci_suspend(struct device *dev) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct cafe_camera *cam = dev_get_drvdata(dev); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci mccic_suspend(&cam->mcam); 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic int __maybe_unused cafe_pci_resume(struct device *dev) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci struct cafe_camera *cam = dev_get_drvdata(dev); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci cafe_ctlr_init(&cam->mcam); 63362306a36Sopenharmony_ci return mccic_resume(&cam->mcam); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic const struct pci_device_id cafe_ids[] = { 63762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 63862306a36Sopenharmony_ci PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) }, 63962306a36Sopenharmony_ci { 0, } 64062306a36Sopenharmony_ci}; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cafe_ids); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cafe_pci_pm_ops, cafe_pci_suspend, cafe_pci_resume); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic struct pci_driver cafe_pci_driver = { 64762306a36Sopenharmony_ci .name = "cafe1000-ccic", 64862306a36Sopenharmony_ci .id_table = cafe_ids, 64962306a36Sopenharmony_ci .probe = cafe_pci_probe, 65062306a36Sopenharmony_ci .remove = cafe_pci_remove, 65162306a36Sopenharmony_ci .driver.pm = &cafe_pci_pm_ops, 65262306a36Sopenharmony_ci}; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic int __init cafe_init(void) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci int ret; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n", 66262306a36Sopenharmony_ci CAFE_VERSION); 66362306a36Sopenharmony_ci ret = pci_register_driver(&cafe_pci_driver); 66462306a36Sopenharmony_ci if (ret) { 66562306a36Sopenharmony_ci printk(KERN_ERR "Unable to register cafe_ccic driver\n"); 66662306a36Sopenharmony_ci goto out; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci ret = 0; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ciout: 67162306a36Sopenharmony_ci return ret; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic void __exit cafe_exit(void) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci pci_unregister_driver(&cafe_pci_driver); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cimodule_init(cafe_init); 68162306a36Sopenharmony_cimodule_exit(cafe_exit); 682