/* * Support for mipi CSI data generator. * * Copyright (c) 2014 Intel Corporation. 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 version * 2 as published by the Free Software Foundation. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ #include #include #include #include #include "pixter.h" #define to_pixter_dev(sd) container_of(sd, struct pixter_device, sd) #define dev_off(m) offsetof(struct pixter_device, m) static struct regmap_config pixter_reg_config = { .reg_bits = 8, .val_bits = 32, .val_format_endian = REGMAP_ENDIAN_NATIVE, }; static struct pixter_format_bridge format_bridge[] = { {"", 0, ATOMISP_INPUT_FORMAT_BINARY_8, 8}, {"RGGB10", V4L2_MBUS_FMT_SRGGB10_1X10, ATOMISP_INPUT_FORMAT_RAW_10, 10}, {"GRBG10", V4L2_MBUS_FMT_SGRBG10_1X10, ATOMISP_INPUT_FORMAT_RAW_10, 10}, {"GBRG10", V4L2_MBUS_FMT_SGBRG10_1X10, ATOMISP_INPUT_FORMAT_RAW_10, 10}, {"BGGR10", V4L2_MBUS_FMT_SBGGR10_1X10, ATOMISP_INPUT_FORMAT_RAW_10, 10}, {"RGGB8", V4L2_MBUS_FMT_SRGGB8_1X8, ATOMISP_INPUT_FORMAT_RAW_8, 8}, {"GRBG8", V4L2_MBUS_FMT_SGRBG8_1X8, ATOMISP_INPUT_FORMAT_RAW_8, 8}, {"GBRG8", V4L2_MBUS_FMT_SGBRG8_1X8, ATOMISP_INPUT_FORMAT_RAW_8, 8}, {"BGGR8", V4L2_MBUS_FMT_SBGGR8_1X8, ATOMISP_INPUT_FORMAT_RAW_8, 8}, {"YUV422_8", V4L2_MBUS_FMT_UYVY8_1X16, ATOMISP_INPUT_FORMAT_YUV422_8, 16}, {"YUV420_8", 0x8001/*For YUV420*/, ATOMISP_INPUT_FORMAT_YUV420_8, 16}, }; static u32 port_to_channel[4] = {1, 0, 2, 0}; static struct pixter_dbgfs dbgfs[] = { {"root", NULL, DBGFS_DIR, 0, 0}, {"fps", "root", DBGFS_DIR, 0, 0}, {"blank", "root", DBGFS_DIR, 0, 0}, {"timing", "root", DBGFS_DIR, 0, 0}, {"fps_ovrd", "fps", DBGFS_FILE, PIXTER_RW, dev_off(dbg_fps.fps_ovrd)}, {"fps", "fps", DBGFS_FILE, PIXTER_RW, dev_off(dbg_fps.fps)}, {"blank_ovrd", "blank", DBGFS_FILE, PIXTER_RW, dev_off(dbg_blank.blank_ovrd)}, {"h_blank", "blank", DBGFS_FILE, PIXTER_RW, dev_off(dbg_blank.h_blank)}, {"v_blank_pre", "blank", DBGFS_FILE, PIXTER_RW, dev_off(dbg_blank.v_blank_pre)}, {"v_blank_post", "blank", DBGFS_FILE, PIXTER_RW, dev_off(dbg_blank.v_blank_post)}, {"mipi_clk", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.mipi_clk)}, {"cont_hs_clk", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.cont_hs_clk)}, {"timing_ovrd", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.timing_ovrd)}, {"pre", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.pre)}, {"post", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.post)}, {"gap", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.gap)}, {"ck_lpx", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.ck_lpx)}, {"ck_prep", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.ck_prep)}, {"ck_zero", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.ck_zero)}, {"ck_trail", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.ck_trail)}, {"dat_lpx", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.dat_lpx)}, {"dat_prep", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.dat_prep)}, {"dat_zero", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.dat_zero)}, {"dat_trail", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.dat_trail)}, {"twakeup", "timing", DBGFS_FILE, PIXTER_RW, dev_off(dbg_timing.twakeup)}, {"mipi_lanes_num", "timing", DBGFS_FILE, PIXTER_RONLY, dev_off(dbg_timing.mipi_lanes_num)}, }; static u32 pixter_get_tx_freq_sel(u32 *freq) { u32 sel; *freq /= 1000000; /* To MHz */ if (*freq < 20) { sel = 1; *freq = 20; } else if (*freq <= 100) { sel = (*freq + 9) / 10 - 1; *freq = 20 + (sel - 1) * 10; } else if (*freq <= 750) { sel = (*freq + 24) / 25 + 5; *freq = 100 + (sel - 9) * 25; } else { sel = 35; *freq = 750; } *freq *= 1000000; return sel; } static int pixter_read_reg(struct v4l2_subdev *sd, u32 reg, u32 *val) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; if (!dev->regmap) return -EIO; ret = regmap_write(dev->regmap, PIXTER_I2C_ADDR, reg | 1); ret |= regmap_read(dev->regmap, PIXTER_I2C_DATA_R, val); if (ret) { dev_dbg(&client->dev, "Read reg failed. reg=0x%04X\n", reg); return ret; } dev_dbg(&client->dev, "read_reg[0x%04X] = 0x%08X\n", reg, *val); return ret; } static int pixter_write_reg(struct v4l2_subdev *sd, u32 reg, u32 val) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; if (!dev->regmap) return -EIO; ret = regmap_write(dev->regmap, PIXTER_I2C_DATA_W, val); ret |= regmap_write(dev->regmap, PIXTER_I2C_ADDR, reg); if (ret) { dev_dbg(&client->dev, "Write reg failed. reg=0x%04X\n", reg); return ret; } dev_dbg(&client->dev, "write_reg[0x%04X] = 0x%08X\n", reg, (u32)val); return ret; } static int pixter_read_buf(struct v4l2_subdev *sd, u32 addr, u32 size, void *buf) { u32 i; int ret = 0; for (i = 0; i < size; i += 4) { ret = pixter_read_reg(sd, addr + i, (u32*)((u8*)buf + i)); if (ret) break; } return ret; } static int pixter_read_mipi_timing(struct v4l2_subdev *sd) { struct pixter_device *dev = to_pixter_dev(sd); u32 reg_val, ch; ch = port_to_channel[dev->mipi_info->port]; pixter_read_reg(sd, PIXTER_TX_CTRL_TIMING(ch), ®_val); dev->dbg_timing.pre = reg_val & 0x7F; dev->dbg_timing.post = (reg_val >> 8) & 0x7F; dev->dbg_timing.gap = (reg_val >> 16) & 0x7F; pixter_read_reg(sd, PIXTER_TX_CK_TIMING(ch), ®_val); dev->dbg_timing.ck_lpx = reg_val & 0x7F; dev->dbg_timing.ck_prep = (reg_val >> 8) & 0x7F; dev->dbg_timing.ck_zero = (reg_val >> 16) & 0x7F; dev->dbg_timing.ck_trail = (reg_val >> 24) & 0x7F; pixter_read_reg(sd, PIXTER_TX_DAT_TIMING(ch), ®_val); dev->dbg_timing.dat_lpx = reg_val & 0x7F; dev->dbg_timing.dat_prep = (reg_val >> 8) & 0x7F; dev->dbg_timing.dat_zero = (reg_val >> 16) & 0x7F; dev->dbg_timing.dat_trail = (reg_val >> 24) & 0x7F; pixter_read_reg(sd, PIXTER_TX_ULPS_TIMING(ch), ®_val); dev->dbg_timing.twakeup = reg_val & 0x7FFFF; return 0; } static int pixter_config_rx(struct v4l2_subdev *sd) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct pixter_setting *setting; u32 h_blank, v_blank_pre, v_blank_post; u32 i, reg_val, ch, vc = 0; u32 line_interval; u32 width_bits; u32 bit_rate; u32 line_bits; setting = &dev->settings[dev->cur_setting]; ch = port_to_channel[dev->mipi_info->port]; /* Set setting start and end address in DDR SDRAM. */ pixter_write_reg(sd, PIXTER_RDR_START(ch), setting->start); pixter_write_reg(sd, PIXTER_RDR_END(ch), setting->end); /* Set FPS. */ if (dev->dbg_fps.fps_ovrd) reg_val = dev->dbg_fps.fps << 24; else reg_val = setting->vc[setting->def_vc].fps << 24; if (setting->block_mode) reg_val |= PIXTER_DFT_BLOCK_MODE; if (dev->caps->sensor[0].stream_num > 1) { /* Select the channel with the largest width. */ for (i = 1; i < 4; i++) { if (setting->vc[i].width > setting->vc[vc].width) vc = i; } } width_bits = setting->vc[vc].width * format_bridge[setting->vc[vc].format].bpp; bit_rate = dev->dbg_timing.mipi_clk / 1000000 * 2 * dev->mipi_info->num_lanes; if (bit_rate == 0 || bit_rate > PIXTER_MAX_BITRATE_MBPS) { dev_err(&client->dev, "Invalid bit rate %dMbps.\n", bit_rate); return -EINVAL; } if (dev->dbg_blank.blank_ovrd) { h_blank = dev->dbg_blank.h_blank; line_bits = 1000 * (width_bits + h_blank * format_bridge[setting->vc[vc].format].bpp); line_interval = line_bits / bit_rate; v_blank_pre = dev->dbg_blank.v_blank_pre; v_blank_post = dev->dbg_blank.v_blank_post; } else { line_bits = 1200 * width_bits; line_interval = line_bits / bit_rate; v_blank_pre = PIXTER_DEF_VBPRE; v_blank_post = line_interval; } /* Set line interval */ reg_val |= (line_interval / 8) << 8; pixter_write_reg(sd, PIXTER_DFT_CTRL(ch), reg_val); /* Set vertical blanking */ reg_val = ((v_blank_post / 8) << 16) + v_blank_pre / 8; pixter_write_reg(sd, PIXTER_VERT_BLANK(ch), reg_val); return 0; } static int pixter_config_tx(struct v4l2_subdev *sd) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); u32 reg_val, ch; u32 cnt = 500; /* Set the parameters in the tx_csi2_ctrl. */ ch = port_to_channel[dev->mipi_info->port]; reg_val = dev->mipi_info->num_lanes - 1; reg_val |= (dev->dbg_timing.cont_hs_clk << 4); if (!dev->dbg_timing.timing_ovrd) reg_val |= 1 << 8; pixter_write_reg(sd, PIXTER_TX_CSI2_CTRL(ch), reg_val); /* Set MIPI timing if overided. */ if (dev->dbg_timing.timing_ovrd) { reg_val = dev->dbg_timing.pre; reg_val |= dev->dbg_timing.post << 8; reg_val |= dev->dbg_timing.gap << 16; pixter_write_reg(sd, PIXTER_TX_CTRL_TIMING(ch), reg_val); reg_val = dev->dbg_timing.ck_lpx; reg_val |= dev->dbg_timing.ck_prep << 8; reg_val |= dev->dbg_timing.ck_zero << 16; reg_val |= dev->dbg_timing.ck_trail << 24; pixter_write_reg(sd, PIXTER_TX_CK_TIMING(ch), reg_val); reg_val = dev->dbg_timing.dat_lpx; reg_val |= dev->dbg_timing.dat_prep << 8; reg_val |= dev->dbg_timing.dat_zero << 16; reg_val |= dev->dbg_timing.dat_trail << 24; pixter_write_reg(sd, PIXTER_TX_DAT_TIMING(ch), reg_val); reg_val = dev->dbg_timing.twakeup; pixter_write_reg(sd, PIXTER_TX_ULPS_TIMING(ch), reg_val); } /* Config MIPI clock. */ reg_val = pixter_get_tx_freq_sel(&dev->dbg_timing.mipi_clk); pixter_write_reg(sd, PIXTER_TX_CTRL(ch), reg_val); /* Wait MIPI clock to be ready. Timeout=5s. */ while (cnt) { pixter_write_reg(sd, PIXTER_TX_CTRL_NNS(ch), 1); pixter_read_reg(sd, PIXTER_TX_STATUS(ch), ®_val); if (reg_val & PIXTER_TX_READY) break; usleep_range(10000, 10000); cnt--; } if (cnt == 0) { dev_err(&client->dev, "Wait MIPI clock ready timeout.\n"); return -EBUSY; } pixter_read_mipi_timing(sd); return 0; } static void __print_mipi_timing(struct v4l2_subdev *sd) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct pixter_timing *dbg_time = &dev->dbg_timing; unsigned short mipi_clk = dbg_time->mipi_clk / 1000000; /* 1UI = 1 bit periold, in pS */ unsigned int ui = 1000000 / (mipi_clk * 2); unsigned int tmp; dev_dbg(&client->dev, "MIPI CLK: %d MHz.\n", mipi_clk); dev_dbg(&client->dev, "----Pixter MIPI Parameters----\n"); dev_dbg(&client->dev, "ck_lpx: %d.\n", dbg_time->ck_lpx); dev_dbg(&client->dev, "ck_prep: %d.\n", dbg_time->ck_prep); dev_dbg(&client->dev, "ck_zero: %d.\n", dbg_time->ck_zero); dev_dbg(&client->dev, "pre: %d.\n", dbg_time->pre); dev_dbg(&client->dev, "post: %d.\n", dbg_time->post); dev_dbg(&client->dev, "ck_trail: %d.\n", dbg_time->ck_trail); dev_dbg(&client->dev, "dat_lpx: %d.\n", dbg_time->dat_lpx); dev_dbg(&client->dev, "dat_prep: %d.\n", dbg_time->dat_prep); dev_dbg(&client->dev, "dat_zero: %d.\n", dbg_time->dat_zero); dev_dbg(&client->dev, "dat_trail: %d.\n", dbg_time->dat_trail); dev_dbg(&client->dev, "gap: %d.\n", dbg_time->gap); dev_dbg(&client->dev, "twakeup: %d.\n", dbg_time->twakeup); dev_dbg(&client->dev, "----Standard MIPI Parameters----\n"); tmp = (dbg_time->ck_lpx + 1) * 8 * ui; dev_dbg(&client->dev, "CLK-LPX: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->ck_prep + 1) * 8 * ui; dev_dbg(&client->dev, "CLK-PREPARE: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->ck_zero + 1) * 8 * ui; dev_dbg(&client->dev, "CLK-ZERO: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->pre - dbg_time->ck_lpx - dbg_time->ck_zero - 3) * 8 * ui; dev_dbg(&client->dev, "CLK-PRE: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->post + 8) * 8 * ui; dev_dbg(&client->dev, "CLK-POST: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->ck_trail + 1) * 8 * ui; dev_dbg(&client->dev, "CLK-TRAIL: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->dat_lpx + 1) * 8 * ui; dev_dbg(&client->dev, "HS-LPX: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->dat_prep + 1) * 8 * ui; dev_dbg(&client->dev, "HS-PREPARE: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->dat_zero + 6) * 8 * ui; dev_dbg(&client->dev, "HS-ZERO: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->dat_trail + 2) * 8 * ui; dev_dbg(&client->dev, "HS-TRAIL: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->gap + 9) * 8 * ui; dev_dbg(&client->dev, "HS-EXIT: %d.%d nS.\n", tmp / 1000, tmp % 1000); tmp = (dbg_time->twakeup + 1) * 8 * ui; dev_dbg(&client->dev, "Wakeup: %d.%d nS.\n", tmp / 1000, tmp % 1000); } static int pixter_s_stream(struct v4l2_subdev *sd, int enable) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = 0; u32 reg_val; dev_dbg(&client->dev, "Set stream for pixter. enable=%d\n", enable); mutex_lock(&dev->input_lock); if (enable) { __print_mipi_timing(sd); ret = pixter_config_rx(sd); if (ret) goto out; ret = pixter_config_tx(sd); if (ret) goto out; } /* Enable stream output. */ reg_val = 1 << port_to_channel[dev->mipi_info->port]; if (!enable) reg_val <<= 4; pixter_write_reg(sd, PIXTER_CPX_CTRL, reg_val); if (!enable) memset(dev->vc_setting, 0, sizeof(dev->vc_setting)); out: mutex_unlock(&dev->input_lock); return ret; } static int pixter_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval) { struct pixter_device *dev = to_pixter_dev(sd); struct pixter_setting *setting; mutex_lock(&dev->input_lock); setting = &dev->settings[dev->cur_setting]; /* Return the currently selected settings' maximum frame interval */ interval->interval.numerator = 1; if (dev->dbg_fps.fps_ovrd) interval->interval.denominator = dev->dbg_fps.fps; else interval->interval.denominator = setting->vc[setting->def_vc].fps; mutex_unlock(&dev->input_lock); return 0; } static int pixter_s_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval) { struct pixter_device *dev = to_pixter_dev(sd); struct pixter_setting *setting; if (interval->interval.numerator == 0) return -EINVAL; mutex_lock(&dev->input_lock); setting = &dev->settings[dev->cur_setting]; setting->vc[setting->def_vc].fps = interval->interval.denominator / interval->interval.numerator; mutex_unlock(&dev->input_lock); return 0; } static int pixter_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code) { struct pixter_device *dev = to_pixter_dev(sd); struct pixter_setting *setting = &dev->settings[dev->cur_setting]; *code = format_bridge[setting->vc[0].format].v4l2_format; return 0; } static u32 pixter_try_mbus_fmt_locked(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct atomisp_input_stream_info *stream_info = (struct atomisp_input_stream_info*)fmt->reserved; struct pixter_setting *settings = dev->settings; struct pixter_vc_setting *vc_setting = dev->vc_setting; u32 vc, i, j; s32 idx = -1, max_idx = -1; s64 w0, h0, mismatch, distance; s64 w1 = fmt->width; s64 h1 = fmt->height; s64 min_distance = LLONG_MAX; dev_dbg(&client->dev, "pixter_try_mbus_fmt. size=%dx%d stream=%d\n", fmt->width, fmt->height, stream_info->stream); if (dev->caps->sensor[0].stream_num == 1) vc = 0; else vc = stream_info->stream; for (i = 0; i < dev->setting_num; i++) { if (dev->setting_en[i] == 0) continue; max_idx = i; for (j = 0; j < 4; j++) { if (!vc_setting[j].width) continue; if (vc_setting[j].width != settings[i].vc[j].width || vc_setting[j].height != settings[i].vc[j].height) break; } if (j < 4) continue; w0 = settings[i].vc[vc].width; h0 = settings[i].vc[vc].height; if (w0 < w1 || h0 < h1) continue; mismatch = abs(w0 * h1 - w1 * h0) * 8192; do_div(mismatch, w1 * h0); if (mismatch > 8192 * PIXTER_MAX_RATIO_MISMATCH / 100) continue; distance = (w0 * h1 + w1 * h0) * 8192; do_div(distance, w1 * h1); if (distance < min_distance) { min_distance = distance; idx = i; } } if (idx < 0 && max_idx < 0) { idx = dev->setting_num - 1; dev_warn(&client->dev, "All settings disabled, using: %dx%d\n", settings[idx].vc[vc].width, settings[idx].vc[vc].height); } else if (idx < 0) { idx = max_idx; dev_warn(&client->dev, "using max enabled resolution: %dx%d\n", settings[idx].vc[vc].width, settings[idx].vc[vc].height); } fmt->width = settings[idx].vc[vc].width; fmt->height = settings[idx].vc[vc].height; fmt->code = format_bridge[settings[idx].vc[vc].format].v4l2_format; return idx; } static int pixter_try_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) { struct pixter_device *dev = to_pixter_dev(sd); mutex_lock(&dev->input_lock); pixter_try_mbus_fmt_locked(sd, fmt); mutex_unlock(&dev->input_lock); return 0; } static int pixter_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct pixter_device *dev = to_pixter_dev(sd); struct atomisp_input_stream_info *stream_info = (struct atomisp_input_stream_info*)fmt->reserved; struct pixter_setting *setting; u32 vc; if (!fmt) return -EINVAL; mutex_lock(&dev->input_lock); if (dev->caps->sensor[0].stream_num == 1) vc = 0; else vc = stream_info->stream; setting = &dev->settings[dev->cur_setting]; fmt->width = setting->vc[vc].width; fmt->height = setting->vc[vc].height; fmt->code = format_bridge[setting->vc[vc].format].v4l2_format; mutex_unlock(&dev->input_lock); dev_dbg(&client->dev, "%s w:%d h:%d code: 0x%x stream: %d\n", __func__, fmt->width, fmt->height, fmt->code, stream_info->stream); return 0; } static int pixter_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct atomisp_input_stream_info *stream_info = (struct atomisp_input_stream_info*)fmt->reserved; if (!fmt) return -EINVAL; mutex_lock(&dev->input_lock); dev->cur_setting = pixter_try_mbus_fmt_locked(sd, fmt); if (dev->caps->sensor[0].stream_num == 1) stream_info->ch_id = 0; else stream_info->ch_id = stream_info->stream; dev->vc_setting[stream_info->ch_id] = dev->settings[dev->cur_setting].vc[stream_info->ch_id]; mutex_unlock(&dev->input_lock); dev_dbg(&client->dev, "%s w:%d h:%d code: 0x%x stream: %d\n", __func__, fmt->width, fmt->height, fmt->code, stream_info->stream); return 0; } static int pixter_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { int ret; u32 reg_val; if (reg->size != 4) return -EINVAL; ret = pixter_read_reg(sd, reg->reg, ®_val); if (ret) return ret; reg->val = reg_val; return 0; } static int pixter_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { if (reg->size != 4) return -EINVAL; return pixter_write_reg(sd, reg->reg, reg->val); } static long pixter_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { switch (cmd) { case ATOMISP_IOC_S_EXPOSURE: break; case ATOMISP_IOC_G_SENSOR_PRIV_INT_DATA: break; case VIDIOC_DBG_G_REGISTER: pixter_g_register(sd, arg); break; case VIDIOC_DBG_S_REGISTER: pixter_s_register(sd, arg); break; default: return -EINVAL; } return 0; } static int pixter_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_mbus_code_enum *code) { struct pixter_device *dev = to_pixter_dev(sd); if (code->index >= 1) return -EINVAL; mutex_lock(&dev->input_lock); code->code = dev->format.code; mutex_unlock(&dev->input_lock); return 0; } static int pixter_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_frame_size_enum *fse) { int index = fse->index; struct pixter_device *dev = to_pixter_dev(sd); mutex_lock(&dev->input_lock); if (index >= 1) { mutex_unlock(&dev->input_lock); return -EINVAL; } fse->min_width = dev->settings[dev->cur_setting].vc[0].width; fse->min_height = dev->settings[dev->cur_setting].vc[0].height; fse->max_width = fse->min_width; fse->max_height = fse->min_height; mutex_unlock(&dev->input_lock); return 0; } static struct v4l2_mbus_framefmt * __pixter_get_pad_format(struct pixter_device *sensor, struct v4l2_subdev_fh *fh, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: return v4l2_subdev_get_try_format(fh, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &sensor->format; default: return NULL; } } static int pixter_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { struct pixter_device *dev = to_pixter_dev(sd); if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) dev->format = fmt->format; return 0; } static int pixter_get_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { struct pixter_device *dev = to_pixter_dev(sd); struct v4l2_mbus_framefmt *format = __pixter_get_pad_format(dev, fh, fmt->pad, fmt->which); fmt->format = *format; return 0; } static int pixter_enum_framesizes(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize) { unsigned int index = fsize->index; struct pixter_device *dev = to_pixter_dev(sd); mutex_lock(&dev->input_lock); if (index >= 1) { mutex_unlock(&dev->input_lock); return -EINVAL; } fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = dev->settings[dev->cur_setting].vc[0].width; fsize->discrete.height = dev->settings[dev->cur_setting].vc[0].height; fsize->reserved[0] = 1; mutex_unlock(&dev->input_lock); return 0; } static int pixter_enum_frameintervals(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival) { struct pixter_device *dev = to_pixter_dev(sd); mutex_lock(&dev->input_lock); fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; fival->width = dev->settings[dev->cur_setting].vc[0].width; fival->height = dev->settings[dev->cur_setting].vc[0].height; fival->discrete.numerator = 1; fival->discrete.denominator = 30; mutex_unlock(&dev->input_lock); return 0; } static int pixter_s_power(struct v4l2_subdev *sd, int on) { struct pixter_device *dev = to_pixter_dev(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); u32 reg_val; dev_dbg(&client->dev, "Set power for pixter. on=%d\n", on); /* Disable channel output. */ reg_val = 1 << (port_to_channel[dev->mipi_info->port] + 4); pixter_write_reg(sd, PIXTER_CPX_CTRL, reg_val); memset(dev->vc_setting, 0, sizeof(dev->vc_setting)); return 0; } static ssize_t pixter_dbgfs_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { struct pixter_dbgfs_data *data = file->f_inode->i_private; struct pixter_device *dev = data->dev; ssize_t ret = 0; u32 *val = (u32*) data->ptr; u32 i; char *str = kzalloc(1024, GFP_KERNEL); if (!str) return 0; if (val >= dev->setting_en && val < &dev->setting_en[dev->setting_num]) { u32 idx = (val - dev->setting_en); struct pixter_setting *setting = &dev->settings[idx]; char sub_str[128]; if (idx >= dev->setting_num) goto out; snprintf(str, 1024, "Valid VCs: %d\n", setting->valid_vc_num); for (i = 0; i < 4; i++) { struct pixter_vc_setting *vc = &setting->vc[i]; if (vc->width == 0) continue; snprintf(sub_str, 128, "VC%d: %dx%d @ %dfps - %s\n", i, vc->width, vc->height, vc->fps, format_bridge[vc->format].name); strncat(str, sub_str, 1023 - strlen(str)); } snprintf(sub_str, 128, "Def: VC%d\nState: %s\n", setting->def_vc, dev->setting_en[idx] ? "Enabled" : "Disabled"); strncat(str, sub_str, 1023 - strlen(str)); } else { snprintf(str, 1024, "%d\n", *val); } ret = simple_read_from_buffer(buf, size, ppos, str, strlen(str)); out: kfree(str); return ret; } static ssize_t pixter_dbgfs_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos) { struct pixter_dbgfs_data *data = file->f_inode->i_private; struct pixter_device *dev = data->dev; u32 *val = (u32*) data->ptr; char str[16] = {0}; ssize_t ret; ret = simple_write_to_buffer(str, 16, ppos, buf, size); sscanf(str, "%d", val); if (val == &dev->dbg_timing.timing_ovrd && *val == 0) pixter_config_tx(&dev->sd); return ret; } static const char * const ctrl_run_mode_menu[] = { NULL, "Video", "Still capture", "Continuous capture", "Preview", }; static const struct v4l2_ctrl_config ctrls[] = { { .id = V4L2_CID_RUN_MODE, .name = "Run Mode", .type = V4L2_CTRL_TYPE_MENU, .min = 1, .def = 4, .max = 4, .qmenu = ctrl_run_mode_menu, } }; static const struct v4l2_subdev_core_ops pixter_core_ops = { .s_power = pixter_s_power, .queryctrl = v4l2_subdev_queryctrl, .g_ctrl = v4l2_subdev_g_ctrl, .s_ctrl = v4l2_subdev_s_ctrl, .ioctl = pixter_ioctl, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = pixter_g_register, .s_register = pixter_s_register, #endif }; static const struct v4l2_subdev_video_ops pixter_video_ops = { .s_stream = pixter_s_stream, .enum_framesizes = pixter_enum_framesizes, .enum_frameintervals = pixter_enum_frameintervals, .enum_mbus_fmt = pixter_enum_mbus_fmt, .try_mbus_fmt = pixter_try_mbus_fmt, .g_mbus_fmt = pixter_g_mbus_fmt, .s_mbus_fmt = pixter_s_mbus_fmt, .g_frame_interval = pixter_g_frame_interval, .s_frame_interval = pixter_s_frame_interval, }; static const struct v4l2_subdev_pad_ops pixter_pad_ops = { .enum_mbus_code = pixter_enum_mbus_code, .enum_frame_size = pixter_enum_frame_size, .get_fmt = pixter_get_pad_format, .set_fmt = pixter_set_pad_format, }; static const struct v4l2_subdev_ops pixter_ops = { .core = &pixter_core_ops, .video = &pixter_video_ops, .pad = &pixter_pad_ops, }; static const struct media_entity_operations pixter_entity_ops = { .link_setup = NULL, }; static const struct file_operations pixter_dbgfs_fops = { .read = pixter_dbgfs_read, .write = pixter_dbgfs_write, .llseek = generic_file_llseek, }; static int pixter_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct pixter_device *dev = to_pixter_dev(sd); if (dev->sd.entity.links) media_entity_cleanup(&dev->sd.entity); dev->platform_data->csi_cfg(sd, 0); v4l2_device_unregister_subdev(sd); if (dev->dbgfs_data) debugfs_remove_recursive(dev->dbgfs_data[0].entry); return 0; } static int pixter_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pixter_device *dev; const struct atomisp_camera_caps *caps = NULL; char *pixter_name = NULL; struct pixter_setting *settings; struct pixter_dbgfs_data *dbgfs_data; u32 reg_val, i, j; int ret; /* allocate sensor device & init sub device */ dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; mutex_init(&dev->input_lock); dev->dbg_timing.mipi_clk = PIXTER_DEF_CLOCK; v4l2_i2c_subdev_init(&dev->sd, client, &pixter_ops); if (client->dev.platform_data) { dev->platform_data = client->dev.platform_data; ret = dev->platform_data->csi_cfg(&dev->sd, 1); if (ret) goto out_free; if (dev->platform_data->get_camera_caps) caps = dev->platform_data->get_camera_caps(); else caps = atomisp_get_default_camera_caps(); dev->caps = caps; } dev->mipi_info = v4l2_get_subdev_hostdata(&dev->sd); if (!dev->mipi_info) { dev_err(&client->dev, "Faild to get mipi info.\n"); goto out_free; } /* Get the number of mipi lanes */ dev->dbg_timing.mipi_lanes_num = dev->mipi_info->num_lanes; dev->regmap = devm_regmap_init_i2c(client, &pixter_reg_config); if (IS_ERR(dev->regmap)) { ret = PTR_ERR(dev->regmap); dev_err(&client->dev, "Failed to allocate register map: %d\n", ret); goto out_free; } /* Load Pixter settings */ pixter_write_reg(&dev->sd, PIXTER_SDRAM_BASE, 0); pixter_read_reg(&dev->sd, PIXTER_MAGIC_ADDR, ®_val); if (reg_val != PIXTER_MAGIC) { dev_err(&client->dev, "PIXTER magic does not match. Got 0x%X\n", reg_val); ret = -EIO; goto out_free; } pixter_read_reg(&dev->sd, PIXTER_SETTING_NUM, &dev->setting_num); dev->settings = devm_kzalloc(&client->dev, sizeof(struct pixter_setting) * dev->setting_num, GFP_KERNEL); if (!dev->settings) { dev_err(&client->dev, "OOM when allocating settings.\n"); ret = -ENOMEM; goto out_free; } settings = dev->settings; ret = pixter_read_buf(&dev->sd, PIXTER_SETTING_START, sizeof(struct pixter_setting) * dev->setting_num, settings); if (ret) { dev_err(&client->dev, "Failed to read Pixter settings\n"); goto out_free; } /* Find settings that match the current device. */ for (i = 0, j = 0; i < dev->setting_num; i++) { if (caps->sensor[0].stream_num == settings[i].valid_vc_num) settings[j++] = settings[i]; } dev->setting_num = j; dev_info(&client->dev, "Setting num=%d\n", dev->setting_num); if (!dev->setting_num) { dev_err(&client->dev, "No matched settings loaded.\n"); ret = -ENODEV; goto out_free; } dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; dev->pad.flags = MEDIA_PAD_FL_SOURCE; dev->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; dev->format.code = format_bridge[ settings[0].vc[settings[0].def_vc].format].v4l2_format; /* * sd->name is updated with sensor driver name by the v4l2. * change it to sensor name in this case. */ if (dev->mipi_info->port == ATOMISP_CAMERA_PORT_PRIMARY) pixter_name = PIXTER_0; else if(dev->mipi_info->port == ATOMISP_CAMERA_PORT_SECONDARY) pixter_name = PIXTER_1; else pixter_name = PIXTER_2; snprintf(dev->sd.name, sizeof(dev->sd.name), "%s %d-%04x", pixter_name, i2c_adapter_id(client->adapter), client->addr); dev_info(&client->dev, "%s dev->sd.name: %s\n", __func__, dev->sd.name); dev->sd.entity.ops = &pixter_entity_ops; dev->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; ret = v4l2_ctrl_handler_init(&dev->ctrl_handler, ARRAY_SIZE(ctrls)); if (ret) goto out_free; for (i = 0; i < ARRAY_SIZE(ctrls); i++) v4l2_ctrl_new_custom(&dev->ctrl_handler, &ctrls[i], NULL); if (dev->ctrl_handler.error) { ret = -EINVAL; goto out_free; } /* Use same lock for controls as for everything else. */ dev->ctrl_handler.lock = &dev->input_lock; dev->sd.ctrl_handler = &dev->ctrl_handler; v4l2_ctrl_handler_setup(&dev->ctrl_handler); ret = media_entity_init(&dev->sd.entity, 1, &dev->pad, 0); if (ret) goto out_free; /* Create debugfs nodes. */ dev->dbgfs_data = devm_kzalloc(&client->dev, sizeof(struct pixter_dbgfs_data) * (ARRAY_SIZE(dbgfs) + dev->setting_num + 1), GFP_KERNEL); if (!dev->dbgfs_data) goto out_free; dbgfs_data = dev->dbgfs_data; dbgfs_data[0].entry = debugfs_create_dir(pixter_name, NULL); for (i = 1; i < ARRAY_SIZE(dbgfs); i++) { struct dentry *parent; for (j = 0; j < i; j++) { if (!strcmp(dbgfs[i].parent, dbgfs[j].name)) break; } if (j == i) continue; parent = dbgfs_data[j].entry; dbgfs_data[i].dev = dev; dbgfs_data[i].ptr = (u8*)dev + dbgfs[i].offset; if (dbgfs[i].type == DBGFS_DIR) dbgfs_data[i].entry = debugfs_create_dir(dbgfs[i].name, parent); else dbgfs_data[i].entry = debugfs_create_file(dbgfs[i].name, dbgfs[i].mode, parent, &dbgfs_data[i], &pixter_dbgfs_fops); } /* Create setting nodes. */ dev->setting_en = devm_kzalloc(&client->dev, sizeof(u32) * dev->setting_num, GFP_KERNEL); if (!dev->setting_en) goto out_free; dbgfs_data[i].entry = debugfs_create_dir("settings", dbgfs_data[0].entry); for (j = 0; j < dev->setting_num; j++) { char setting_name[32]; u32 idx = i + j + 1; struct pixter_vc_setting *vc = &settings[j].vc[settings[j].def_vc]; dev->setting_en[j] = 1; snprintf(setting_name, 32, "%d.%dx%d_%s@%d", j, vc->width, vc->height, format_bridge[vc->format].name, vc->fps); dbgfs_data[idx].dev = dev; dbgfs_data[idx].ptr = &dev->setting_en[j]; dbgfs_data[idx].entry = debugfs_create_file(setting_name, S_IRUSR|S_IWUSR, dbgfs_data[i].entry, &dbgfs_data[idx], &pixter_dbgfs_fops); } pixter_read_mipi_timing(&dev->sd); return 0; out_free: pixter_remove(client); return ret; } static const struct i2c_device_id pixter_ids[] = { {PIXTER_0, 0}, {PIXTER_1, 0}, {PIXTER_2, 0}, {} }; MODULE_DEVICE_TABLE(i2c, pixter_ids); static struct i2c_driver pixter_driver = { .driver = { .owner = THIS_MODULE, .name = PIXTER_DRV, }, .probe = pixter_probe, .remove = pixter_remove, .id_table = pixter_ids, }; module_i2c_driver(pixter_driver); MODULE_DESCRIPTION("Pixter MIPI CSI simulator driver."); MODULE_AUTHOR("Tianshu Qiu "); MODULE_LICENSE("GPL");