cregit-Linux how code gets into the kernel

Release 4.11 drivers/media/i2c/soc_camera/imx074.c

/*
 * Driver for IMX074 CMOS Image Sensor from Sony
 *
 * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
 *
 * Partially inspired by the IMX074 driver from the Android / MSM tree
 *
 * 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.
 */

#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/v4l2-mediabus.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/module.h>

#include <media/soc_camera.h>
#include <media/v4l2-async.h>
#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>

/* IMX074 registers */


#define MODE_SELECT			0x0100

#define IMAGE_ORIENTATION		0x0101

#define GROUPED_PARAMETER_HOLD		0x0104

/* Integration Time */

#define COARSE_INTEGRATION_TIME_HI	0x0202

#define COARSE_INTEGRATION_TIME_LO	0x0203
/* Gain */

#define ANALOGUE_GAIN_CODE_GLOBAL_HI	0x0204

#define ANALOGUE_GAIN_CODE_GLOBAL_LO	0x0205

/* PLL registers */

#define PRE_PLL_CLK_DIV			0x0305

#define PLL_MULTIPLIER			0x0307

#define PLSTATIM			0x302b

#define VNDMY_ABLMGSHLMT		0x300a

#define Y_OPBADDR_START_DI		0x3014
/* mode setting */

#define FRAME_LENGTH_LINES_HI		0x0340

#define FRAME_LENGTH_LINES_LO		0x0341

#define LINE_LENGTH_PCK_HI		0x0342

#define LINE_LENGTH_PCK_LO		0x0343

#define YADDR_START			0x0347

#define YADDR_END			0x034b

#define X_OUTPUT_SIZE_MSB		0x034c

#define X_OUTPUT_SIZE_LSB		0x034d

#define Y_OUTPUT_SIZE_MSB		0x034e

#define Y_OUTPUT_SIZE_LSB		0x034f

#define X_EVEN_INC			0x0381

#define X_ODD_INC			0x0383

#define Y_EVEN_INC			0x0385

#define Y_ODD_INC			0x0387


#define HMODEADD			0x3001

#define VMODEADD			0x3016

#define VAPPLINE_START			0x3069

#define VAPPLINE_END			0x306b

#define SHUTTER				0x3086

#define HADDAVE				0x30e8

#define LANESEL				0x3301

/* IMX074 supported geometry */

#define IMX074_WIDTH			1052

#define IMX074_HEIGHT			780

/* IMX074 has only one fixed colorspace per pixelcode */

struct imx074_datafmt {
	
u32	code;
	
enum v4l2_colorspace		colorspace;
};


struct imx074 {
	
struct v4l2_subdev		subdev;
	
const struct imx074_datafmt	*fmt;
	
struct v4l2_clk			*clk;
};


static const struct imx074_datafmt imx074_colour_fmts[] = {
	{MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
};


static struct imx074 *to_imx074(const struct i2c_client *client) { return container_of(i2c_get_clientdata(client), struct imx074, subdev); }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski28100.00%1100.00%
Total28100.00%1100.00%

/* Find a data format by a pixel code in an array */
static const struct imx074_datafmt *imx074_find_datafmt(u32 code) { int i; for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++) if (imx074_colour_fmts[i].code == code) return imx074_colour_fmts + i; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski4998.00%150.00%
Boris Brezillon12.00%150.00%
Total50100.00%2100.00%


static int reg_write(struct i2c_client *client, const u16 addr, const u8 data) { struct i2c_adapter *adap = client->adapter; struct i2c_msg msg; unsigned char tx[3]; int ret; msg.addr = client->addr; msg.buf = tx; msg.len = 3; msg.flags = 0; tx[0] = addr >> 8; tx[1] = addr & 0xff; tx[2] = data; ret = i2c_transfer(adap, &msg, 1); mdelay(2); return ret == 1 ? 0 : -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski120100.00%1100.00%
Total120100.00%1100.00%


static int reg_read(struct i2c_client *client, const u16 addr) { u8 buf[2] = {addr >> 8, addr & 0xff}; int ret; struct i2c_msg msgs[] = { { .addr = client->addr, .flags = 0, .len = 2, .buf = buf, }, { .addr = client->addr, .flags = I2C_M_RD, .len = 2, .buf = buf, }, }; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); if (ret < 0) { dev_warn(&client->dev, "Reading register %x from %x failed\n", addr, client->addr); return ret; } return buf[0] & 0xff; /* no sign-extension */ }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski141100.00%1100.00%
Total141100.00%1100.00%


static int imx074_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code); struct i2c_client *client = v4l2_get_subdevdata(sd); struct imx074 *priv = to_imx074(client); if (format->pad) return -EINVAL; dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code); if (!fmt) { /* MIPI CSI could have changed the format, double-check */ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; mf->code = imx074_colour_fmts[0].code; mf->colorspace = imx074_colour_fmts[0].colorspace; } mf->width = IMX074_WIDTH; mf->height = IMX074_HEIGHT; mf->field = V4L2_FIELD_NONE; if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) priv->fmt = imx074_find_datafmt(mf->code); else cfg->try_fmt = *mf; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski10859.67%150.00%
Hans Verkuil7340.33%150.00%
Total181100.00%2100.00%


static int imx074_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct imx074 *priv = to_imx074(client); const struct imx074_datafmt *fmt = priv->fmt; if (format->pad) return -EINVAL; mf->code = fmt->code; mf->colorspace = fmt->colorspace; mf->width = IMX074_WIDTH; mf->height = IMX074_HEIGHT; mf->field = V4L2_FIELD_NONE; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski8074.07%150.00%
Hans Verkuil2825.93%150.00%
Total108100.00%2100.00%


static int imx074_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) return -EINVAL; sel->r.left = 0; sel->r.top = 0; sel->r.width = IMX074_WIDTH; sel->r.height = IMX074_HEIGHT; switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP: return 0; default: return -EINVAL; } }

Contributors

PersonTokensPropCommitsCommitProp
Hans Verkuil4651.11%150.00%
Guennadi Liakhovetski4448.89%150.00%
Total90100.00%2100.00%


static int imx074_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->pad || (unsigned int)code->index >= ARRAY_SIZE(imx074_colour_fmts)) return -EINVAL; code->code = imx074_colour_fmts[code->index].code; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski4371.67%150.00%
Hans Verkuil1728.33%150.00%
Total60100.00%2100.00%


static int imx074_s_stream(struct v4l2_subdev *sd, int enable) { struct i2c_client *client = v4l2_get_subdevdata(sd); /* MODE_SELECT: stream or standby */ return reg_write(client, MODE_SELECT, !!enable); }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski37100.00%1100.00%
Total37100.00%1100.00%


static int imx074_s_power(struct v4l2_subdev *sd, int on) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct imx074 *priv = to_imx074(client); return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); }

Contributors

PersonTokensPropCommitsCommitProp
Laurent Pinchart4370.49%133.33%
Guennadi Liakhovetski1829.51%266.67%
Total61100.00%3100.00%


static int imx074_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { cfg->type = V4L2_MBUS_CSI2; cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski35100.00%1100.00%
Total35100.00%1100.00%

static struct v4l2_subdev_video_ops imx074_subdev_video_ops = { .s_stream = imx074_s_stream, .g_mbus_config = imx074_g_mbus_config, }; static struct v4l2_subdev_core_ops imx074_subdev_core_ops = { .s_power = imx074_s_power, }; static const struct v4l2_subdev_pad_ops imx074_subdev_pad_ops = { .enum_mbus_code = imx074_enum_mbus_code, .get_selection = imx074_get_selection, .get_fmt = imx074_get_fmt, .set_fmt = imx074_set_fmt, }; static struct v4l2_subdev_ops imx074_subdev_ops = { .core = &imx074_subdev_core_ops, .video = &imx074_subdev_video_ops, .pad = &imx074_subdev_pad_ops, };
static int imx074_video_probe(struct i2c_client *client) { struct v4l2_subdev *subdev = i2c_get_clientdata(client); int ret; u16 id; ret = imx074_s_power(subdev, 1); if (ret < 0) return ret; /* Read sensor Model ID */ ret = reg_read(client, 0); if (ret < 0) goto done; id = ret << 8; ret = reg_read(client, 1); if (ret < 0) goto done; id |= ret; dev_info(&client->dev, "Chip ID 0x%04x detected\n", id); if (id != 0x74) { ret = -ENODEV; goto done; } /* PLL Setting EXTCLK=24MHz, 22.5times */ reg_write(client, PLL_MULTIPLIER, 0x2D); reg_write(client, PRE_PLL_CLK_DIV, 0x02); reg_write(client, PLSTATIM, 0x4B); /* 2-lane mode */ reg_write(client, 0x3024, 0x00); reg_write(client, IMAGE_ORIENTATION, 0x00); /* select RAW mode: * 0x08+0x08 = top 8 bits * 0x0a+0x08 = compressed 8-bits * 0x0a+0x0a = 10 bits */ reg_write(client, 0x0112, 0x08); reg_write(client, 0x0113, 0x08); /* Base setting for High frame mode */ reg_write(client, VNDMY_ABLMGSHLMT, 0x80); reg_write(client, Y_OPBADDR_START_DI, 0x08); reg_write(client, 0x3015, 0x37); reg_write(client, 0x301C, 0x01); reg_write(client, 0x302C, 0x05); reg_write(client, 0x3031, 0x26); reg_write(client, 0x3041, 0x60); reg_write(client, 0x3051, 0x24); reg_write(client, 0x3053, 0x34); reg_write(client, 0x3057, 0xC0); reg_write(client, 0x305C, 0x09); reg_write(client, 0x305D, 0x07); reg_write(client, 0x3060, 0x30); reg_write(client, 0x3065, 0x00); reg_write(client, 0x30AA, 0x08); reg_write(client, 0x30AB, 0x1C); reg_write(client, 0x30B0, 0x32); reg_write(client, 0x30B2, 0x83); reg_write(client, 0x30D3, 0x04); reg_write(client, 0x3106, 0x78); reg_write(client, 0x310C, 0x82); reg_write(client, 0x3304, 0x05); reg_write(client, 0x3305, 0x04); reg_write(client, 0x3306, 0x11); reg_write(client, 0x3307, 0x02); reg_write(client, 0x3308, 0x0C); reg_write(client, 0x3309, 0x06); reg_write(client, 0x330A, 0x08); reg_write(client, 0x330B, 0x04); reg_write(client, 0x330C, 0x08); reg_write(client, 0x330D, 0x06); reg_write(client, 0x330E, 0x01); reg_write(client, 0x3381, 0x00); /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */ /* 1608 = 1560 + 48 (black lines) */ reg_write(client, FRAME_LENGTH_LINES_HI, 0x06); reg_write(client, FRAME_LENGTH_LINES_LO, 0x48); reg_write(client, YADDR_START, 0x00); reg_write(client, YADDR_END, 0x2F); /* 0x838 == 2104 */ reg_write(client, X_OUTPUT_SIZE_MSB, 0x08); reg_write(client, X_OUTPUT_SIZE_LSB, 0x38); /* 0x618 == 1560 */ reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06); reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18); reg_write(client, X_EVEN_INC, 0x01); reg_write(client, X_ODD_INC, 0x03); reg_write(client, Y_EVEN_INC, 0x01); reg_write(client, Y_ODD_INC, 0x03); reg_write(client, HMODEADD, 0x00); reg_write(client, VMODEADD, 0x16); reg_write(client, VAPPLINE_START, 0x24); reg_write(client, VAPPLINE_END, 0x53); reg_write(client, SHUTTER, 0x00); reg_write(client, HADDAVE, 0x80); reg_write(client, LANESEL, 0x00); reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */ ret = 0; done: imx074_s_power(subdev, 0); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski62991.82%150.00%
Laurent Pinchart568.18%150.00%
Total685100.00%2100.00%


static int imx074_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct imx074 *priv; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); int ret; if (!ssdd) { dev_err(&client->dev, "IMX074: missing platform data!\n"); return -EINVAL; } if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_warn(&adapter->dev, "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); return -EIO; } priv = devm_kzalloc(&client->dev, sizeof(struct imx074), GFP_KERNEL); if (!priv) return -ENOMEM; v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); priv->fmt = &imx074_colour_fmts[0]; priv->clk = v4l2_clk_get(&client->dev, "mclk"); if (IS_ERR(priv->clk)) { dev_info(&client->dev, "Error %ld getting clock\n", PTR_ERR(priv->clk)); return -EPROBE_DEFER; } ret = soc_camera_power_init(&client->dev, ssdd); if (ret < 0) goto epwrinit; ret = imx074_video_probe(client); if (ret < 0) goto eprobe; ret = v4l2_async_register_subdev(&priv->subdev); if (!ret) return 0; epwrinit: eprobe: v4l2_clk_put(priv->clk); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski261100.00%7100.00%
Total261100.00%7100.00%


static int imx074_remove(struct i2c_client *client) { struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); struct imx074 *priv = to_imx074(client); v4l2_async_unregister_subdev(&priv->subdev); v4l2_clk_put(priv->clk); if (ssdd->free_bus) ssdd->free_bus(ssdd); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski62100.00%5100.00%
Total62100.00%5100.00%

static const struct i2c_device_id imx074_id[] = { { "imx074", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, imx074_id); static struct i2c_driver imx074_i2c_driver = { .driver = { .name = "imx074", }, .probe = imx074_probe, .remove = imx074_remove, .id_table = imx074_id, }; module_i2c_driver(imx074_i2c_driver); MODULE_DESCRIPTION("Sony IMX074 Camera driver"); MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); MODULE_LICENSE("GPL v2");

Overall Contributors

PersonTokensPropCommitsCommitProp
Guennadi Liakhovetski199786.71%950.00%
Hans Verkuil1988.60%422.22%
Laurent Pinchart1024.43%211.11%
Boris Brezillon30.13%15.56%
Axel Lin20.09%15.56%
Linus Torvalds10.04%15.56%
Total2303100.00%18100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.