diff options
author | Xinyu Chen <xinyu.chen@freescale.com> | 2012-04-20 13:02:04 +0800 |
---|---|---|
committer | Xinyu Chen <xinyu.chen@freescale.com> | 2012-04-20 13:02:04 +0800 |
commit | b0cf397cecdecc102b82fc844b7a9e098d841187 (patch) | |
tree | 681e410089711d7e8d13cd819004e698fa6fec14 /drivers/media | |
parent | eb1da050c08bb961a1e45dd51f97821d3eeb2ae9 (diff) | |
parent | a7fbbad7276b41e8b338afafcaedf8c005de5c48 (diff) |
Merge remote branch 'fsl-linux-sdk/imx_3.0.15_12.04.01' into imx_3.0.15_android
Conflicts:
arch/arm/kernel/traps.c
arch/arm/mach-mx6/board-mx6q_sabresd.c
arch/arm/mach-mx6/cpu.c
arch/arm/mach-mx6/system.c
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/radio/Kconfig | 13 | ||||
-rw-r--r-- | drivers/media/radio/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/radio/si4763-i2c.c | 933 | ||||
-rw-r--r-- | drivers/media/radio/si4763-i2c.h | 249 | ||||
-rw-r--r-- | drivers/media/video/mxc/output/mxc_vout.c | 263 |
5 files changed, 1441 insertions, 18 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 52798a111e16..4aefb00cc6cf 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -355,6 +355,19 @@ config RADIO_SI470X bool "Silicon Labs Si470x FM Radio Receiver support" depends on VIDEO_V4L2 +config I2C_SI4763 + tristate "Silicon Labs Si4763 AMFM Radio Transmitter support" + depends on I2C && VIDEO_V4L2 + ---help--- + Say Y here if you want support to Si4763 AMFM Radio Transmitter. + This device can transmit audio through AM or FM. + This module is the v4l2 radio + interface for the i2c driver of this device. + + To compile this driver as a module, choose M here: the + module will be called radio-si4763. + + source "drivers/media/radio/si470x/Kconfig" config USB_MR800 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index f484a6e04eb2..07cd9ef0e19a 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o obj-$(CONFIG_RADIO_TRUST) += radio-trust.o obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o +obj-$(CONFIG_I2C_SI4763) += si4763-i2c.o obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o diff --git a/drivers/media/radio/si4763-i2c.c b/drivers/media/radio/si4763-i2c.c new file mode 100644 index 000000000000..c4990a3eee5d --- /dev/null +++ b/drivers/media/radio/si4763-i2c.c @@ -0,0 +1,933 @@ +/* + * Copyright (C) 2010-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/regulator/consumer.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-common.h> + +#include "si4763-i2c.h" + +#define DEBUG 1 +/* module parameters */ +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0 - 2)"); + +MODULE_DESCRIPTION("I2C driver for Si4763 FM Radio Transmitter"); + +/* Radio Nr */ +static int radio_nr = -1; + + +/* radio_si4713_fops - file operations interface */ +static const struct v4l2_file_operations radio_si4763_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, +}; + + +#define DEFAULT_RDS_PI 0x00 +#define DEFAULT_RDS_PTY 0x00 +#define DEFAULT_RDS_PS_NAME "" +#define DEFAULT_RDS_RADIO_TEXT DEFAULT_RDS_PS_NAME +#define DEFAULT_RDS_DEVIATION 0x00C8 +#define DEFAULT_RDS_PS_REPEAT_COUNT 0x0003 +#define DEFAULT_LIMITER_RTIME 0x1392 +#define DEFAULT_LIMITER_DEV 0x102CA +#define DEFAULT_PILOT_FREQUENCY 0x4A38 +#define DEFAULT_PILOT_DEVIATION 0x1A5E +#define DEFAULT_ACOMP_ATIME 0x0000 +#define DEFAULT_ACOMP_RTIME 0xF4240L +#define DEFAULT_ACOMP_GAIN 0x0F +#define DEFAULT_ACOMP_THRESHOLD (-0x28) +#define DEFAULT_MUTE 0x01 +#define DEFAULT_POWER_LEVEL 88 +#define DEFAULT_FREQUENCY 8800 +#define DEFAULT_PREEMPHASIS FMPE_EU +#define DEFAULT_TUNE_RNL 0xFF + +#define to_si4763_device(sd) container_of(sd, struct si4763_device, sd) + +/* frequency domain transformation (using times 10 to avoid floats) */ +#define FREQ_RANGE_LOW 8750 +#define FREQ_RANGE_HIGH 10800 + +#define MAX_ARGS 7 + +#define RDS_BLOCK 8 +#define RDS_BLOCK_CLEAR 0x03 +#define RDS_BLOCK_LOAD 0x04 +#define RDS_RADIOTEXT_2A 0x20 +#define RDS_RADIOTEXT_BLK_SIZE 4 +#define RDS_RADIOTEXT_INDEX_MAX 0x0F +#define RDS_CARRIAGE_RETURN 0x0D + +#define rds_ps_nblocks(len) ((len / RDS_BLOCK) + (len % RDS_BLOCK ? 1 : 0)) + +#define get_status_bit(p, b, m) (((p) & (m)) >> (b)) +#define set_bits(p, v, b, m) (((p) & ~(m)) | ((v) << (b))) + +#define ATTACK_TIME_UNIT 500 + +#define POWER_OFF 0x00 +#define POWER_ON 0x01 + +#define msb(x) ((u8)((u16) x >> 8)) +#define lsb(x) ((u8)((u16) x & 0x00FF)) +#define compose_u16(msb, lsb) (((u16)msb << 8) | lsb) +#define compose_u32(msb, lsb) (((u32)msb << 16) | lsb) +#define check_command_failed(status) (!(status & SI4713_CTS) || \ + (status & SI4713_ERR)) +/* mute definition */ +#define set_mute(p) ((p & 1) | ((p & 1) << 1)); +#define get_mute(p) (p & 0x01) + +#ifdef DEBUG +#define DBG_BUFFER(device, message, buffer, size) \ + { \ + int i; \ + char str[(size)*5]; \ + for (i = 0; i < size; i++) \ + sprintf(str + i * 5, " 0x%02x", buffer[i]); \ + v4l2_dbg(2, debug, device, "%s:%s\n", message, str); \ + } +#else +#define DBG_BUFFER(device, message, buffer, size) +#endif + + +/* + * si4713_send_command - sends a command to si4713 and waits its response + * @sdev: si4713_device structure for the device we are communicating + * @command: command id + * @args: command arguments we are sending (up to 7) + * @argn: actual size of @args + * @response: buffer to place the expected response from the device (up to 15) + * @respn: actual size of @response + * @usecs: amount of time to wait before reading the response (in usecs) + */ +static int si4713_send_command(struct si4763_device *sdev, const u8 command, + const u8 args[], const int argn, + u8 response[], const int respn, const int usecs) +{ + struct i2c_client *client = sdev->client; + u8 data1[MAX_ARGS + 1]; + int err; + int i; + if (!client->adapter) + return -ENODEV; + + /* First send the command and its arguments */ + data1[0] = command; + memcpy(data1 + 1, args, argn); + DBG_BUFFER(&sdev->sd, "Parameters", data1, argn + 1); + + err = i2c_master_send(client, data1, argn + 1); + if (err != argn + 1) { + v4l2_err(&sdev->sd, "Error while sending command 0x%02x\n", + command); + return (err > 0) ? -EIO : err; + } + + /* Wait response from interrupt */ +/* + if (!wait_for_completion_timeout(&sdev->work, + usecs_to_jiffies(usecs) + 1)) + v4l2_warn(&sdev->sd, + "(%s) Device took too much time to answer.\n", + __func__); + +*/ + + + /* Then get the response */ + i = 0; + err = i2c_master_recv(client, response, respn); + while (i++ < usecs && ((response[0] & 0x80) != 0x80)) { + err = i2c_master_recv(client, response, respn); + udelay(1); + } + + + if (err != respn) { + v4l2_err(&sdev->sd, + "Error while reading response for command 0x%02x\n", + command); + return (err > 0) ? -EIO : err; + } + + DBG_BUFFER(&sdev->sd, "Response", response, respn); + if (check_command_failed(response[0])) + return -EBUSY; + + return 0; +} + + +/* + * si4713_write_property - modifies a si4713 property + * @sdev: si4713_device structure for the device we are communicating + * @prop: property identification number + * @val: new value for that property + */ +static int si4713_write_property(struct si4763_device *sdev, u16 prop, u16 val) +{ + int rval; + u8 resp[SI4713_SET_PROP_NRESP]; + /* + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + * .Fourth byte = value's MSB + * .Fifth byte = value's LSB + */ + const u8 args[SI4713_SET_PROP_NARGS] = { + 0x00, + msb(prop), + lsb(prop), + msb(val), + lsb(val), + }; + + rval = si4713_send_command(sdev, SI4713_CMD_SET_PROPERTY, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: property=0x%02x value=0x%02x status=0x%02x\n", + __func__, prop, val, resp[0]); + + /* + * As there is no command response for SET_PROPERTY, + * wait Tcomp time to finish before proceed, in order + * to have property properly set. + */ + msleep(TIMEOUT_SET_PROPERTY); + + return rval; +} + +static int si4713_enable_digitalout(struct si4763_device *sdev) +{ + int rval; + u8 resp[SI4713_SET_PROP_NRESP]; + /* + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + * .Fourth byte = value's MSB + * .Fifth byte = value's LSB + */ + const u8 args[SI4713_EN_DIGITALOUT_NARGS] = { + 10, + 10, + 12, + 0, + }; + + rval = si4713_send_command(sdev, SI4713_CMD_EN_DIGITALOUT, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + + /* + * As there is no command response for SET_PROPERTY, + * wait Tcomp time to finish before proceed, in order + * to have property properly set. + */ + msleep(TIMEOUT_SET_PROPERTY); + + return rval; +} + +static int si4713_get_digitalout(struct si4763_device *sdev, u32 *response) +{ + int rval; + u8 resp[5]; + u16 temp1, temp2; + + /* + * .First byte = 0 + * .Second byte = property's MSB + * .Third byte = property's LSB + * .Fourth byte = value's MSB + * .Fifth byte = value's LSB + */ + const u8 args[SI4713_EN_DIGITALOUT_NARGS] = { + 0, + 0, + 0, + 0, + }; + + rval = si4713_send_command(sdev, SI4713_CMD_EN_DIGITALOUT, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + return rval; + + temp1 = compose_u16(resp[1], resp[2]); + temp2 = compose_u16(resp[3], resp[4]); + *response = compose_u32(temp1, temp2); + /* + * As there is no command response for SET_PROPERTY, + * wait Tcomp time to finish before proceed, in order + * to have property properly set. + */ + msleep(TIMEOUT_SET_PROPERTY); + + return rval; +} + + +/* + * si4713_powerup - Powers the device up + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_powerup(struct si4763_device *sdev) +{ + int err; + u8 resp[SI4760_PWUP_NRESP]; + const u8 args[SI4760_PWUP_NARGS] = { + SI4760_PWUP_ARG1, + SI4760_PWUP_ARG2, + SI4760_PWUP_ARG3, + SI4760_PWUP_ARG4, + SI4760_PWUP_ARG5, + }; + + + if (sdev->power_state) + return 0; + err = si4713_send_command(sdev, SI4760_CMD_POWER_UP, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + TIMEOUT_POWER_UP); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, "Powerup response: 0x%02x\n", + resp[0]); + v4l2_dbg(1, debug, &sdev->sd, "Device in power up mode\n"); + sdev->power_state = POWER_ON; + + } else { + /* if (gpio_is_valid(sdev->gpio_reset)) + gpio_set_value(sdev->gpio_reset, 0); + err = regulator_bulk_disable(ARRAY_SIZE(sdev->supplies), + sdev->supplies); + */ if (err) + v4l2_err(&sdev->sd, + "Failed to disable supplies: %d\n", err); + } + + return err; +} + +/* + * si4713_powerdown - Powers the device down + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_powerdown(struct si4763_device *sdev) +{ + int err; + u8 resp[SI4760_PWDN_NRESP]; + + + err = si4713_send_command(sdev, SI4760_CMD_POWER_DOWN, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (!err) { + v4l2_dbg(1, debug, &sdev->sd, "Power down response: 0x%02x\n", + resp[0]); + v4l2_dbg(1, debug, &sdev->sd, "Device in reset mode\n"); + if (err) + v4l2_err(&sdev->sd, + "Failed to disable supplies: %d\n", err); + sdev->power_state = POWER_OFF; + } + + return err; +} + +/* + * si4713_checkrev - Checks if we are treating a device with the correct rev. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_checkrev(struct si4763_device *sdev) +{ + struct i2c_client *client = sdev->client; + int rval; + u8 resp[SI4760_GETREV_NRESP]; + + mutex_lock(&sdev->mutex); + + rval = si4713_send_command(sdev, SI4760_CMD_GET_REV, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (rval < 0) + goto unlock; + + if (resp[1] == SI4760_PRODUCT_NUMBER) { + v4l2_info(&sdev->sd, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + } else { + v4l2_err(&sdev->sd, "Invalid product number\n"); + rval = -EINVAL; + } + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +/* + * si4713_wait_stc - Waits STC interrupt and clears status bits. Usefull + * for TX_TUNE_POWER, TX_TUNE_FREQ and TX_TUNE_MEAS + * @sdev: si4713_device structure for the device we are communicating + * @usecs: timeout to wait for STC interrupt signal + */ +static int si4713_wait_stc(struct si4763_device *sdev, const int usecs) +{ + int err; + u8 resp[SI4713_GET_STATUS_NRESP]; + + /* Wait response from STC interrupt */ + if (!wait_for_completion_timeout(&sdev->work, + usecs_to_jiffies(usecs) + 1)) + v4l2_warn(&sdev->sd, + "%s: device took too much time to answer (%d usec).\n", + __func__, usecs); + + /* Clear status bits */ + err = si4713_send_command(sdev, SI4713_CMD_GET_INT_STATUS, + NULL, 0, + resp, ARRAY_SIZE(resp), + DEFAULT_TIMEOUT); + + if (err < 0) + goto exit; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: status bits: 0x%02x\n", __func__, resp[0]); + + if (!(resp[0] & SI4713_STC_INT)) + err = -EIO; + +exit: + return err; +} + +/* + * si4713_tx_tune_freq - Sets the state of the RF carrier and sets the tuning + * frequency between 76 and 108 MHz in 10 kHz units and + * steps of 50 kHz. + * @sdev: si4713_device structure for the device we are communicating + * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) + */ +static int si4713_tx_tune_freq(struct si4763_device *sdev, u16 frequency) +{ + int err; + u8 val[SI4713_TXFREQ_NRESP]; + /* + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB + */ + const u8 args[SI4713_TXFREQ_NARGS] = { + 0x00, + msb(frequency), + lsb(frequency), + 0x00, + 0x00, + }; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_FREQ, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + return 0; +} + + +/* + * si4713_tx_tune_measure - Enters receive mode and measures the received noise + * level in units of dBuV on the selected frequency. + * The Frequency must be between 76 and 108 MHz in 10 kHz + * units and steps of 50 kHz. The command also sets the + * antenna tuning capacitance. A value of 0 means + * autotuning, and a value of 1 to 191 indicates manual + * override. + * @sdev: si4713_device structure for the device we are communicating + * @frequency: desired frequency (76 - 108 MHz, unit 10 KHz, step 50 kHz) + * @antcap: value of antenna tuning capacitor (0 - 191) + */ +static int si4713_tx_tune_measure(struct si4763_device *sdev, u16 frequency, + u8 antcap) +{ + int err; + u8 val[SI4713_TXMEA_NRESP]; + /* + * .First byte = 0 + * .Second byte = frequency's MSB + * .Third byte = frequency's LSB + * .Fourth byte = antcap + */ + const u8 args[SI4713_TXMEA_NARGS] = { + 0x00, + msb(frequency), + lsb(frequency), + antcap, + }; + + sdev->tune_rnl = DEFAULT_TUNE_RNL; + + if (antcap > SI4713_MAX_ANTCAP) + return -EDOM; + + err = si4713_send_command(sdev, SI4713_CMD_TX_TUNE_MEASURE, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + if (err < 0) + return err; + + v4l2_dbg(1, debug, &sdev->sd, + "%s: frequency=0x%02x antcap=0x%02x status=0x%02x\n", + __func__, frequency, antcap, val[0]); + + return si4713_wait_stc(sdev, TIMEOUT_TX_TUNE); +} + +static int si4763_fm_rsq_status(struct si4763_device *sdev, + u16 *frequency) +{ + int err; + u8 val[SI4763_FM_RSQ_STATUS_NRESP]; + /* + * .First byte = intack bit + */ + const u8 args[SI4763_FM_RSQ_STATUS_NARGS] = { + 0x00, + }; + + err = si4713_send_command(sdev, SI4763_CMD_FM_RSQ_STATUS, + args, ARRAY_SIZE(args), val, + ARRAY_SIZE(val), DEFAULT_TIMEOUT); + + *frequency = compose_u16(val[3], val[4]); + return err; +} + + +static int si4713_set_power_state(struct si4763_device *sdev, u8 value) +{ + int rval; + + mutex_lock(&sdev->mutex); + + if (value) + rval = si4713_powerup(sdev); + else + rval = si4713_powerdown(sdev); + + mutex_unlock(&sdev->mutex); + return rval; +} + + +/* + * si4713_initialize - Sets the device up with default configuration. + * @sdev: si4713_device structure for the device we are communicating + */ +static int si4713_initialize(struct si4763_device *sdev) +{ + int rval; + + rval = si4713_set_power_state(sdev, POWER_OFF); + if (rval < 0) + goto exit; + + rval = si4713_set_power_state(sdev, POWER_ON); + if (rval < 0) + goto exit; + + rval = si4713_checkrev(sdev); + if (rval < 0) + goto exit; + rval = si4713_set_power_state(sdev, POWER_OFF); + if (rval < 0) + goto exit; + +exit: + return rval; +} + +static int si4763_setup(struct si4763_device *sdev) +{ + int rval; + u32 temp; + rval = si4713_write_property(sdev, 0x0202, 0xBB80); + if (rval < 0) + goto exit; + + rval = si4713_write_property(sdev, 0x0203, 0x0400); + if (rval < 0) + goto exit; + rval = si4713_tx_tune_freq(sdev, FREQ_RANGE_LOW); + if (rval < 0) + goto exit; + + rval = si4713_enable_digitalout(sdev); + if (rval < 0) + goto exit; + + rval = si4713_get_digitalout(sdev, &temp); + if (rval < 0) + goto exit; + +exit: + return rval; +} + +/* + * Video4Linux Subdev Interface + */ + +/* si4713_ioctl - deal with private ioctls (only rnl for now) */ +long si4713_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct si4763_device *sdev = to_si4763_device(sd); + struct si4713_rnl *rnl = arg; + int frequency = 0; + int rval = 0; + + if (!arg) + return -EINVAL; + + mutex_lock(&sdev->mutex); + switch (cmd) { + case SI4713_IOC_MEASURE_RNL: + + if (sdev->power_state) { + /* Set desired measurement frequency */ + rval = si4713_tx_tune_measure(sdev, frequency, 0); + if (rval < 0) + goto unlock; + /* get results from tune status */ + } + rnl->rnl = sdev->tune_rnl; + break; + + default: + /* nothing */ + rval = -ENOIOCTLCMD; + } + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + + +/* si4763_g_frequency - get tuner or modulator radio frequency */ +static int si4763_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + + struct si4763_device *sdev = video_drvdata(file); + int rval = 0; + + f->type = V4L2_TUNER_RADIO; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + u16 freq; + + rval = si4763_fm_rsq_status(sdev, &freq); + if (rval < 0) + goto unlock; + + sdev->frequency = freq; + } + + f->frequency = sdev->frequency; + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + +/* si4713_s_frequency - set tuner or modulator radio frequency */ +static int si4763_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) + +{ + struct si4763_device *sdev = video_drvdata(file); + int rval = 0; + u16 frequency = f->frequency; + + /* Check frequency range */ + if (frequency < FREQ_RANGE_LOW || frequency > FREQ_RANGE_HIGH) + return -EDOM; + + mutex_lock(&sdev->mutex); + + if (sdev->power_state) { + rval = si4713_tx_tune_freq(sdev, frequency); + if (rval < 0) + goto unlock; + frequency = rval; + rval = 0; + } + sdev->frequency = frequency; + f->frequency = frequency; + +unlock: + mutex_unlock(&sdev->mutex); + return rval; +} + + +/************************************************************************** + * File Operations Interface + **************************************************************************/ + +/* + * si4763_fops_open - file open + */ +int si4763_fops_open(struct file *file) +{ + struct si4763_device *radio = video_drvdata(file); + int retval = 0; + + if (radio->users != 0) + return -ENODEV; + /* start radio */ + si4713_set_power_state(radio, POWER_ON); + si4763_setup(radio); + radio->users = 1; + + return retval; +} + + +/* + * si470x_fops_release - file release + */ +int si4763_fops_release(struct file *file) +{ + struct si4763_device *radio = video_drvdata(file); + int retval = 0; + + /* safety check */ + if (!radio) + return -ENODEV; + + + /* stop radio */ + si4713_set_power_state(radio, POWER_OFF); + radio->users = 0; + return retval; +} + + +/* + * si4763_fops - file operations interface + * video_ioctl2 is part of the v4l2 implementations. Change this pointer to the + * ioctl function you want to implement, in case you don't want to be part of + * v4l2. + */ +static const struct v4l2_file_operations si4763_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, + .open = si4763_fops_open, + .release = si4763_fops_release, +}; + + +/* + * si4763_ioctl_ops - video device ioctl operations + */ +static const struct v4l2_ioctl_ops si4763_ioctl_ops = { + .vidioc_g_frequency = si4763_g_frequency, + .vidioc_s_frequency = si4763_s_frequency, +}; + + +/* + * si4763_viddev_template - video device interface + */ +struct video_device si4763_viddev_template = { + .fops = &si4763_fops, + .name = "si4763_i2c", + .release = video_device_release, + .ioctl_ops = &si4763_ioctl_ops, +}; + + +/* + * I2C driver interface + */ +/* si4713_probe - probe for the device */ +static int si4713_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct si4763_device *sdev; + int rval; + + sdev = kzalloc(sizeof *sdev, GFP_KERNEL); + if (!sdev) { + dev_err(&client->dev, "Failed to alloc video device.\n"); + rval = -ENOMEM; + goto exit; + } + sdev->client = client; + sdev->users = 0; + /* video device allocation and initialization */ + sdev->videodev = video_device_alloc(); + if (!sdev->videodev) { + rval = -ENOMEM; + goto free_video; + } + + + mutex_init(&sdev->mutex); + memcpy(sdev->videodev, &si4763_viddev_template, + sizeof(si4763_viddev_template)); + video_set_drvdata(sdev->videodev, sdev); +/* + if (client->irq) { + rval = request_irq(client->irq, + si4713_handler, IRQF_TRIGGER_FALLING | IRQF_DISABLED, + client->name, sdev); + if (rval < 0) { + v4l2_err(&sdev->sd, "Could not request IRQ\n"); + goto put_reg; + } + v4l2_dbg(1, debug, &sdev->sd, "IRQ requested.\n"); + } else { + v4l2_warn(&sdev->sd, "IRQ not configured. Using timeouts.\n"); + } +*/ + + rval = si4713_initialize(sdev); + if (rval < 0) { + v4l2_err(&sdev->sd, "Failed to probe device information.\n"); + goto free_sdev; + } + + /* register video device */ + rval = video_register_device(sdev->videodev, VFL_TYPE_RADIO, + radio_nr); + if (rval) { + dev_warn(&client->dev, "Could not register video device\n"); + goto free_video; + } + i2c_set_clientdata(client, sdev); + return 0; + +free_video: + video_device_release(sdev->videodev); + +free_sdev: + kfree(sdev); +exit: + return rval; +} + +/* si4713_remove - remove the device */ +static int si4713_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct si4763_device *sdev = to_si4763_device(sd); + + if (sdev->power_state) + si4713_set_power_state(sdev, POWER_DOWN); + + if (client->irq > 0) + free_irq(client->irq, sdev); + + v4l2_device_unregister_subdev(sd); + kfree(sdev); + + return 0; +} + +/* si4713_i2c_driver - i2c driver interface */ +static const struct i2c_device_id si4713_id[] = { + { "si4763_i2c" , 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, si4713_id); + +static struct i2c_driver si4713_i2c_driver = { + .driver = { + .name = "si4763_i2c", + }, + .probe = si4713_probe, + .remove = si4713_remove, + .id_table = si4713_id, +}; + +/* Module Interface */ +static int __init si4713_module_init(void) +{ + return i2c_add_driver(&si4713_i2c_driver); +} + +static void __exit si4713_module_exit(void) +{ + i2c_del_driver(&si4713_i2c_driver); +} + +module_init(si4713_module_init); +module_exit(si4713_module_exit); + +/* Module information */ +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("I2C si4763"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/radio/si4763-i2c.h b/drivers/media/radio/si4763-i2c.h new file mode 100644 index 000000000000..d97eb35863da --- /dev/null +++ b/drivers/media/radio/si4763-i2c.h @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +#ifndef SI4763_I2C_H +#define SI4763_I2C_H + +#include <media/v4l2-subdev.h> +#include <media/si4763.h> + +#define SI4760_PRODUCT_NUMBER 0x05 + +/* Command Timeouts */ +#define DEFAULT_TIMEOUT 500 +#define TIMEOUT_SET_PROPERTY 20 +#define TIMEOUT_TX_TUNE_POWER 30000 +#define TIMEOUT_TX_TUNE 110000 +#define TIMEOUT_POWER_UP 200000 + +/* + * Command and its arguments definitions + */ +#define SI4713_PWUP_CTSIEN (1<<7) +#define SI4713_PWUP_GPO2OEN (1<<6) +#define SI4713_PWUP_PATCH (1<<5) +#define SI4713_PWUP_XOSCEN (1<<4) +#define SI4713_PWUP_FUNC_TX 0x02 +#define SI4713_PWUP_FUNC_PATCH 0x0F +#define SI4713_PWUP_OPMOD_ANALOG 0x50 +#define SI4713_PWUP_OPMOD_DIGITAL 0x0F +#define SI4760_PWUP_NARGS 5 +#define SI4760_PWUP_NRESP 1 +#define SI4760_PWDOWN_NARGS 1 + +#define SI4760_CMD_POWER_UP 0x01 + +#define SI4760_PWUP_ARG1 0xF7 +#define SI4760_PWUP_ARG2 0x28 +#define SI4760_PWUP_ARG3 0x07 +#define SI4760_PWUP_ARG4 0x11 +#define SI4760_PWUP_ARG5 0x11 + +#define SI4713_CMD_EN_DIGITALOUT 0x18 +#define SI4713_EN_DIGITALOUT_NARGS 4 + + +#define SI4760_GETREV_NRESP 9 +#define SI4760_CMD_GET_REV 0x02 + +#define SI4760_PWDN_NRESP 1 +#define SI4760_CMD_POWER_DOWN 0x11 + +#define SI4763_CMD_FM_RSQ_STATUS 0x32 +#define SI4763_FM_RSQ_STATUS_NARGS 1 +#define SI4763_FM_RSQ_STATUS_NRESP 5 + +#define SI4713_SET_PROP_NARGS 5 +#define SI4713_SET_PROP_NRESP 1 +#define SI4713_CMD_SET_PROPERTY 0x13 + +#define SI4713_GET_PROP_NARGS 3 +#define SI4713_GET_PROP_NRESP 4 +#define SI4713_CMD_GET_PROPERTY 0x13 + +#define SI4713_GET_STATUS_NRESP 1 +#define SI4713_CMD_GET_INT_STATUS 0x14 + +#define SI4713_CMD_PATCH_ARGS 0x15 +#define SI4713_CMD_PATCH_DATA 0x16 + +#define SI4713_MAX_FREQ 10800 +#define SI4713_MIN_FREQ 7600 +#define SI4713_TXFREQ_NARGS 5 +#define SI4713_TXFREQ_NRESP 1 +#define SI4713_CMD_TX_TUNE_FREQ 0x30 + +#define SI4713_MAX_POWER 120 +#define SI4713_MIN_POWER 88 +#define SI4713_MAX_ANTCAP 191 +#define SI4713_MIN_ANTCAP 0 +#define SI4713_TXPWR_NARGS 4 +#define SI4713_TXPWR_NRESP 1 +#define SI4713_CMD_TX_TUNE_POWER 0x31 + +#define SI4713_TXMEA_NARGS 4 +#define SI4713_TXMEA_NRESP 1 +#define SI4713_CMD_TX_TUNE_MEASURE 0x32 + +#define SI4713_INTACK_MASK 0x01 +#define SI4713_TXSTATUS_NARGS 1 +#define SI4713_TXSTATUS_NRESP 8 +#define SI4713_CMD_TX_TUNE_STATUS 0x33 + +#define SI4713_OVERMOD_BIT (1 << 2) +#define SI4713_IALH_BIT (1 << 1) +#define SI4713_IALL_BIT (1 << 0) +#define SI4713_ASQSTATUS_NARGS 1 +#define SI4713_ASQSTATUS_NRESP 5 +#define SI4713_CMD_TX_ASQ_STATUS 0x34 + +#define SI4713_RDSBUFF_MODE_MASK 0x87 +#define SI4713_RDSBUFF_NARGS 7 +#define SI4713_RDSBUFF_NRESP 6 +#define SI4713_CMD_TX_RDS_BUFF 0x35 + +#define SI4713_RDSPS_PSID_MASK 0x1F +#define SI4713_RDSPS_NARGS 5 +#define SI4713_RDSPS_NRESP 1 +#define SI4713_CMD_TX_RDS_PS 0x36 + +#define SI4713_CMD_GPO_CTL 0x80 +#define SI4713_CMD_GPO_SET 0x81 + +/* + * Bits from status response + */ +#define SI4713_CTS (1<<7) +#define SI4713_ERR (1<<6) +#define SI4713_RDS_INT (1<<2) +#define SI4713_ASQ_INT (1<<1) +#define SI4713_STC_INT (1<<0) + +/* + * Property definitions + */ +#define SI4713_GPO_IEN 0x0001 +#define SI4713_DIG_INPUT_FORMAT 0x0101 +#define SI4713_DIG_INPUT_SAMPLE_RATE 0x0103 +#define SI4713_REFCLK_FREQ 0x0201 +#define SI4713_REFCLK_PRESCALE 0x0202 +#define SI4713_TX_COMPONENT_ENABLE 0x2100 +#define SI4713_TX_AUDIO_DEVIATION 0x2101 +#define SI4713_TX_PILOT_DEVIATION 0x2102 +#define SI4713_TX_RDS_DEVIATION 0x2103 +#define SI4713_TX_LINE_INPUT_LEVEL 0x2104 +#define SI4713_TX_LINE_INPUT_MUTE 0x2105 +#define SI4713_TX_PREEMPHASIS 0x2106 +#define SI4713_TX_PILOT_FREQUENCY 0x2107 +#define SI4713_TX_ACOMP_ENABLE 0x2200 +#define SI4713_TX_ACOMP_THRESHOLD 0x2201 +#define SI4713_TX_ACOMP_ATTACK_TIME 0x2202 +#define SI4713_TX_ACOMP_RELEASE_TIME 0x2203 +#define SI4713_TX_ACOMP_GAIN 0x2204 +#define SI4713_TX_LIMITER_RELEASE_TIME 0x2205 +#define SI4713_TX_ASQ_INTERRUPT_SOURCE 0x2300 +#define SI4713_TX_ASQ_LEVEL_LOW 0x2301 +#define SI4713_TX_ASQ_DURATION_LOW 0x2302 +#define SI4713_TX_ASQ_LEVEL_HIGH 0x2303 +#define SI4713_TX_ASQ_DURATION_HIGH 0x2304 +#define SI4713_TX_RDS_INTERRUPT_SOURCE 0x2C00 +#define SI4713_TX_RDS_PI 0x2C01 +#define SI4713_TX_RDS_PS_MIX 0x2C02 +#define SI4713_TX_RDS_PS_MISC 0x2C03 +#define SI4713_TX_RDS_PS_REPEAT_COUNT 0x2C04 +#define SI4713_TX_RDS_PS_MESSAGE_COUNT 0x2C05 +#define SI4713_TX_RDS_PS_AF 0x2C06 +#define SI4713_TX_RDS_FIFO_SIZE 0x2C07 + +#define PREEMPHASIS_USA 75 +#define PREEMPHASIS_EU 50 +#define PREEMPHASIS_DISABLED 0 +#define FMPE_USA 0x00 +#define FMPE_EU 0x01 +#define FMPE_DISABLED 0x02 + +#define POWER_UP 0x01 +#define POWER_DOWN 0x00 + +struct rds_info { + u32 pi; +#define MAX_RDS_PTY 31 + u32 pty; +#define MAX_RDS_DEVIATION 90000 + u32 deviation; +/* + * PSNAME is known to be defined as 8 character sized (RDS Spec). + * However, there is receivers which scroll PSNAME 8xN sized. + */ +#define MAX_RDS_PS_NAME 96 + u8 ps_name[MAX_RDS_PS_NAME + 1]; +/* + * MAX_RDS_RADIO_TEXT is known to be defined as 32 (2A group) or 64 (2B group) + * character sized (RDS Spec). + * However, there is receivers which scroll them as well. + */ +#define MAX_RDS_RADIO_TEXT 384 + u8 radio_text[MAX_RDS_RADIO_TEXT + 1]; + u32 enabled; +}; + +struct limiter_info { +#define MAX_LIMITER_RELEASE_TIME 102390 + u32 release_time; +#define MAX_LIMITER_DEVIATION 90000 + u32 deviation; + u32 enabled; +}; + +struct pilot_info { +#define MAX_PILOT_DEVIATION 90000 + u32 deviation; +#define MAX_PILOT_FREQUENCY 19000 + u32 frequency; + u32 enabled; +}; + +struct acomp_info { +#define MAX_ACOMP_RELEASE_TIME 1000000 + u32 release_time; +#define MAX_ACOMP_ATTACK_TIME 5000 + u32 attack_time; +#define MAX_ACOMP_THRESHOLD 0 +#define MIN_ACOMP_THRESHOLD (-40) + s32 threshold; +#define MAX_ACOMP_GAIN 20 + u32 gain; + u32 enabled; +}; + +#define SI4763_NUM_SUPPLIES 2 + +/* + * si4763_device - private data + */ +struct si4763_device { + /* v4l2_subdev and i2c reference (v4l2_subdev priv data) */ + struct v4l2_subdev sd; + /* private data structures */ + struct mutex mutex; + struct completion work; + struct rds_info rds_info; + struct limiter_info limiter_info; + struct pilot_info pilot_info; + struct acomp_info acomp_info; + struct regulator_bulk_data supplies[SI4763_NUM_SUPPLIES]; + int gpio_reset; + u32 frequency; + u32 preemphasis; + u32 mute; + u32 power_level; + u32 power_state; + u32 antenna_capacitor; + u32 stereo; + u32 tune_rnl; + u8 users; + struct i2c_client *client; + struct video_device *videodev; +}; +#endif /* ifndef SI4763_I2C_H */ diff --git a/drivers/media/video/mxc/output/mxc_vout.c b/drivers/media/video/mxc/output/mxc_vout.c index 43b53678d5ca..7542ca5f2062 100644 --- a/drivers/media/video/mxc/output/mxc_vout.c +++ b/drivers/media/video/mxc/output/mxc_vout.c @@ -30,6 +30,22 @@ #define MAX_FB_NUM 6 #define FB_BUFS 3 +#define VALID_HEIGHT_1080P (1080) +#define FRAME_HEIGHT_1080P (1088) +#define FRAME_WIDTH_1080P (1920) +#define MAX_INTERLACED_WIDTH (1024) +#define CHECK_TILED_1080P_DISPLAY(vout) \ + (((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) && \ + ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ + ((vout)->task.output.crop.w == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ + ((vout)->task.output.crop.h == VALID_HEIGHT_1080P)) +#define CHECK_TILED_1080P_STREAM(vout) \ + (((vout)->task.input.format == IPU_PIX_FMT_TILED_NV12) && \ + ((vout)->task.input.width == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.crop.w == FRAME_WIDTH_1080P) && \ + ((vout)->task.input.height == FRAME_HEIGHT_1080P) && \ + ((vout)->task.input.crop.h == FRAME_HEIGHT_1080P)) struct mxc_vout_fb { char *name; @@ -62,7 +78,14 @@ struct mxc_vout_output { bool fmt_init; bool bypass_pp; + bool is_vdoaipu_task; struct ipu_task task; + struct ipu_task vdoa_task; + struct vdoa_mem { + void *vaddr; + dma_addr_t paddr; + size_t size; + } vdoa_dma; bool timer_stop; struct timer_list timer; @@ -97,7 +120,7 @@ static int video_nr = 16; /* Module parameters */ module_param(video_nr, int, S_IRUGO); MODULE_PARM_DESC(video_nr, "video device numbers"); -module_param(debug, bool, S_IRUGO); +module_param(debug, int, 0600); MODULE_PARM_DESC(debug, "Debug level (0-1)"); const static struct v4l2_fmtdesc mxc_formats[] = { @@ -145,6 +168,14 @@ const static struct v4l2_fmtdesc mxc_formats[] = { .description = "YUV420", .pixelformat = V4L2_PIX_FMT_YUV420, }, + { + .description = "TILED NV12P", + .pixelformat = IPU_PIX_FMT_TILED_NV12, + }, + { + .description = "TILED NV12F", + .pixelformat = IPU_PIX_FMT_TILED_NV12F, + }, }; #define NUM_MXC_VOUT_FORMATS (ARRAY_SIZE(mxc_formats)) @@ -158,6 +189,24 @@ static struct mxc_vout_fb g_fb_setting[MAX_FB_NUM]; static int config_disp_output(struct mxc_vout_output *vout); static void release_disp_output(struct mxc_vout_output *vout); +static unsigned int get_frame_size(struct mxc_vout_output *vout) +{ + unsigned int size; + + if (IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) + size = TILED_NV12_FRAME_SIZE(vout->task.input.width, + vout->task.input.height); + else if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { + size = TILED_NV12_FRAME_SIZE(vout->task.input.width, + vout->task.input.height/2); + size *= 2; + } else + size = vout->task.input.width * vout->task.input.height * + fmt_to_bpp(vout->task.input.format)/8; + + return size; +} + static ipu_channel_t get_ipu_channel(struct fb_info *fbi) { ipu_channel_t ipu_ch = CHAN_NONE; @@ -315,6 +364,9 @@ static bool deinterlace_3_field(struct mxc_vout_output *vout) static bool is_pp_bypass(struct mxc_vout_output *vout) { + if ((IPU_PIX_FMT_TILED_NV12 == vout->task.input.format) || + (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format)) + return false; if ((vout->task.input.width == vout->task.output.width) && (vout->task.input.height == vout->task.output.height) && (vout->task.input.crop.w == vout->task.output.crop.w) && @@ -374,6 +426,8 @@ static int show_buf(struct mxc_vout_output *vout, int idx, struct fb_info *fbi = vout->fbi; struct fb_var_screeninfo var; int ret; + u32 is_1080p; + u32 yres = 0; memcpy(&var, &fbi->var, sizeof(var)); @@ -390,9 +444,17 @@ static int show_buf(struct mxc_vout_output *vout, int idx, ret = fb_pan_display(fbi, &var); console_unlock(); } else { - var.yoffset = idx * fbi->var.yres; console_lock(); + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) { + yres = fbi->var.yres; + fbi->var.yres = FRAME_HEIGHT_1080P; + } + + var.yoffset = idx * fbi->var.yres; ret = fb_pan_display(fbi, &var); + if (is_1080p) + fbi->var.yres = yres; console_unlock(); } @@ -408,6 +470,8 @@ static void disp_work_func(struct work_struct *work) unsigned long flags = 0; struct ipu_pos ipos; int ret = 0; + u32 is_1080p; + u32 ocrop_h = 0; v4l2_dbg(1, debug, vout->vfd->v4l2_dev, "disp work begin one frame\n"); @@ -458,11 +522,31 @@ static void disp_work_func(struct work_struct *work) } vout->task.output.paddr = vout->disp_bufs[vout->frame_count % FB_BUFS]; + + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) { + vout->task.input.crop.h = FRAME_HEIGHT_1080P; + vout->task.output.height = FRAME_HEIGHT_1080P; + ocrop_h = vout->task.output.crop.h; + vout->task.output.crop.h = FRAME_HEIGHT_1080P; + } + if (vout->is_vdoaipu_task) { + vout->vdoa_task.input.paddr = vout->task.input.paddr; + vout->vdoa_task.output.paddr = vout->vdoa_dma.paddr; + ret = ipu_queue_task(&vout->vdoa_task); + if (ret < 0) { + mutex_unlock(&vout->task_lock); + goto err; + } + vout->task.input.paddr = vout->vdoa_task.output.paddr; + } ret = ipu_queue_task(&vout->task); if (ret < 0) { mutex_unlock(&vout->task_lock); goto err; } + if (is_1080p) + vout->task.output.crop.h = ocrop_h; } mutex_unlock(&vout->task_lock); @@ -569,6 +653,7 @@ static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { struct mxc_vout_output *vout = q->priv_data; + unsigned int frame_size; if (!vout) return -EINVAL; @@ -576,8 +661,8 @@ static int mxc_vout_buffer_setup(struct videobuf_queue *q, unsigned int *count, if (V4L2_BUF_TYPE_VIDEO_OUTPUT != q->type) return -EINVAL; - *size = PAGE_ALIGN(vout->task.input.width * vout->task.input.height * - fmt_to_bpp(vout->task.input.format)/8); + frame_size = get_frame_size(vout); + *size = PAGE_ALIGN(frame_size); return 0; } @@ -758,8 +843,7 @@ static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, f->fmt.pix.width = vout->task.input.width; f->fmt.pix.height = vout->task.input.height; f->fmt.pix.pixelformat = vout->task.input.format; - f->fmt.pix.sizeimage = vout->task.input.width * vout->task.input.height * - fmt_to_bpp(vout->task.input.format)/8; + f->fmt.pix.sizeimage = get_frame_size(vout); if (f->fmt.pix.priv) { rect = (struct v4l2_rect *)f->fmt.pix.priv; @@ -768,6 +852,10 @@ static int mxc_vidioc_g_fmt_vid_out(struct file *file, void *fh, rect->width = vout->task.input.crop.w; rect->height = vout->task.input.crop.h; } + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "frame_size:0x%x, pix_fmt:0x%x\n", + f->fmt.pix.sizeimage, + vout->task.input.format); return 0; } @@ -813,33 +901,128 @@ again: return ret; } +static inline int vdoaipu_try_task(struct mxc_vout_output *vout) +{ + int ret; + u32 icrop_h = 0, icrop_w = 0; + int is_1080p_stream; + size_t size; + struct ipu_task *ipu_task = &vout->task; + struct ipu_task *vdoa_task = &vout->vdoa_task; + + is_1080p_stream = CHECK_TILED_1080P_STREAM(vout); + if (is_1080p_stream) + ipu_task->input.crop.h = VALID_HEIGHT_1080P; + + if (ipu_task->input.crop.h % IPU_PIX_FMT_TILED_NV12_MBALIGN) { + icrop_h = ipu_task->input.crop.h; + ipu_task->input.crop.h = ALIGN(ipu_task->input.crop.h, + IPU_PIX_FMT_TILED_NV12_MBALIGN); + } + if (ipu_task->input.crop.w % IPU_PIX_FMT_TILED_NV12_MBALIGN) { + icrop_w = ipu_task->input.crop.w; + ipu_task->input.crop.w = ALIGN(ipu_task->input.crop.w, + IPU_PIX_FMT_TILED_NV12_MBALIGN); + } + + memset(vdoa_task, 0, sizeof(*vdoa_task)); + memcpy(&vdoa_task->input, &ipu_task->input, sizeof(ipu_task->input)); + vdoa_task->output.format = IPU_PIX_FMT_NV12; + vdoa_task->output.width = ipu_task->input.crop.w; + vdoa_task->output.height = ipu_task->input.crop.h; + vdoa_task->output.crop.w = ipu_task->input.crop.w; + vdoa_task->output.crop.h = ipu_task->input.crop.h; + + size = PAGE_ALIGN(ipu_task->input.crop.w * + ipu_task->input.crop.h * + fmt_to_bpp(vdoa_task->output.format)/8); + if (size > vout->vdoa_dma.size) { + if (vout->vdoa_dma.vaddr) { + dma_free_coherent(vout->vbq.dev, vout->vdoa_dma.size, + vout->vdoa_dma.vaddr, vout->vdoa_dma.paddr); + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "free vdoa_dma.size:0x%x, paddr:0x%x\n", + vout->vdoa_dma.size, + vout->vdoa_dma.paddr); + memset(&vout->vdoa_dma, 0, sizeof(vout->vdoa_dma)); + } + vout->vdoa_dma.size = size; + vout->vdoa_dma.vaddr = dma_alloc_coherent(vout->vbq.dev, + vout->vdoa_dma.size, + &vout->vdoa_dma.paddr, + GFP_DMA | GFP_KERNEL); + if (!vout->vdoa_dma.vaddr) + return -ENOMEM; + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "alloc vdoa_dma.size:0x%x, paddr:0x%x\n", + vout->vdoa_dma.size, + vout->vdoa_dma.paddr); + } + ret = ipu_check_task(vdoa_task); + if (ret != IPU_CHECK_OK) + return -EINVAL; + + ipu_task->input.format = vdoa_task->output.format; + if (icrop_h) { + ipu_task->input.height = vdoa_task->output.height; + ipu_task->input.crop.h = icrop_h; + } + if (icrop_w) { + ipu_task->input.width = vdoa_task->output.width; + ipu_task->input.crop.w = icrop_w; + } + ret = ipu_try_task(vout); + + return ret; +} + static int mxc_vout_try_task(struct mxc_vout_output *vout) { int ret = 0; + struct ipu_output *output = &vout->task.output; + struct ipu_input *input = &vout->task.input; - vout->task.input.crop.w -= vout->task.input.crop.w%8; - vout->task.input.crop.h -= vout->task.input.crop.h%8; - + input->crop.w -= input->crop.w%8; + input->crop.h -= input->crop.h%8; /* assume task.output already set by S_CROP */ if (is_pp_bypass(vout)) { v4l2_info(vout->vfd->v4l2_dev, "Bypass IC.\n"); vout->bypass_pp = true; - vout->task.output.format = vout->task.input.format; + output->format = input->format; } else { /* if need CSC, choose IPU-DP or IPU_IC do it */ vout->bypass_pp = false; if (vout->disp_support_csc) { - if (colorspaceofpixel(vout->task.input.format) == YUV_CS) - vout->task.output.format = IPU_PIX_FMT_UYVY; + if (colorspaceofpixel(input->format) == YUV_CS) + output->format = IPU_PIX_FMT_UYVY; else - vout->task.output.format = IPU_PIX_FMT_RGB565; + output->format = IPU_PIX_FMT_RGB565; } else { if (colorspaceofpixel(vout->disp_fmt) == YUV_CS) - vout->task.output.format = IPU_PIX_FMT_UYVY; + output->format = IPU_PIX_FMT_UYVY; + else + output->format = IPU_PIX_FMT_RGB565; + } + + vout->is_vdoaipu_task = 0; + if ((IPU_PIX_FMT_TILED_NV12 == input->format) || + (IPU_PIX_FMT_TILED_NV12F == input->format)) { + /* check resize/rotate/flip, or csc task */ + if ((IPU_ROTATE_NONE != output->rotate) || + (input->crop.w != output->crop.w) || + (input->crop.h != output->crop.h) || + (!vout->disp_support_csc && + (colorspaceofpixel(vout->disp_fmt) == RGB_CS))) + vout->is_vdoaipu_task = 1; else - vout->task.output.format = IPU_PIX_FMT_RGB565; + /* IC bypass */ + output->format = IPU_PIX_FMT_NV12; } - ret = ipu_try_task(vout); + + if (vout->is_vdoaipu_task) + ret = vdoaipu_try_task(vout); + else + ret = ipu_try_task(vout); } return ret; @@ -849,11 +1032,23 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format { int ret = 0; struct v4l2_rect *rect = NULL; + u32 o_height = 0; + u32 ocrop_h = 0; + u32 is_1080p; vout->task.input.width = f->fmt.pix.width; vout->task.input.height = f->fmt.pix.height; vout->task.input.format = f->fmt.pix.pixelformat; + if (IPU_PIX_FMT_TILED_NV12F == vout->task.input.format) { + if (vout->task.input.width > MAX_INTERLACED_WIDTH) + return -EINVAL; + v4l2_info(vout->vfd->v4l2_dev, + "tiled fmt enable deinterlace.\n"); + vout->task.input.deinterlace.enable = true; + vout->task.input.deinterlace.field_fmt = + IPU_DEINTERLACE_FIELD_TOP; + } switch (f->fmt.pix.field) { /* Images are in progressive format, not interlaced */ case V4L2_FIELD_NONE: @@ -893,6 +1088,15 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format vout->task.input.crop.h = f->fmt.pix.height; } + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) { + vout->task.input.crop.h = FRAME_HEIGHT_1080P; + o_height = vout->task.output.height; + ocrop_h = vout->task.output.crop.h; + vout->task.output.height = FRAME_HEIGHT_1080P; + vout->task.output.crop.h = FRAME_HEIGHT_1080P; + } + ret = mxc_vout_try_task(vout); if (!ret) { if (rect) { @@ -904,6 +1108,11 @@ static int mxc_vout_try_format(struct mxc_vout_output *vout, struct v4l2_format } } + if (is_1080p) { + vout->task.output.height = o_height; + vout->task.output.crop.h = ocrop_h; + } + return ret; } @@ -1311,6 +1520,7 @@ static int config_disp_output(struct mxc_vout_output *vout) struct fb_info *fbi = vout->fbi; struct fb_var_screeninfo var; int i, display_buf_size, fb_num, ret; + u32 is_1080p; memcpy(&var, &fbi->var, sizeof(var)); @@ -1332,7 +1542,11 @@ static int config_disp_output(struct mxc_vout_output *vout) } else { fb_num = FB_BUFS; var.xres_virtual = var.xres; - var.yres_virtual = fb_num * var.yres; + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) + var.yres_virtual = fb_num * FRAME_HEIGHT_1080P; + else + var.yres_virtual = fb_num * var.yres; var.vmode &= ~FB_VMODE_YWRAP; } var.bits_per_pixel = fmt_to_bpp(vout->task.output.format); @@ -1357,7 +1571,11 @@ static int config_disp_output(struct mxc_vout_output *vout) if (ret < 0) return ret; - display_buf_size = fbi->fix.line_length * fbi->var.yres; + is_1080p = CHECK_TILED_1080P_DISPLAY(vout); + if (is_1080p) + display_buf_size = fbi->fix.line_length * FRAME_HEIGHT_1080P; + else + display_buf_size = fbi->fix.line_length * fbi->var.yres; for (i = 0; i < fb_num; i++) vout->disp_bufs[i] = fbi->fix.smem_start + i * display_buf_size; @@ -1515,6 +1733,15 @@ static void mxc_vout_free_output(struct mxc_vout_dev *dev) for (i = 0; i < dev->out_num; i++) { vout = dev->out[i]; vfd = vout->vfd; + if (vout->vdoa_dma.vaddr) { + dma_free_coherent(vout->vbq.dev, vout->vdoa_dma.size, + vout->vdoa_dma.vaddr, vout->vdoa_dma.paddr); + v4l2_dbg(1, debug, vout->vfd->v4l2_dev, + "free vdoa_dma.size:0x%x, paddr:0x%x\n", + vout->vdoa_dma.size, + vout->vdoa_dma.paddr); + memset(&vout->vdoa_dma, 0, sizeof(vout->vdoa_dma)); + } if (vfd) { if (!video_is_registered(vfd)) video_device_release(vfd); |