1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "imx227.h"
6 #include "imx227-seq.h"
7 #include <ddk/binding.h>
8 #include <ddk/debug.h>
9 #include <ddk/metadata.h>
10 #include <ddk/metadata/camera.h>
11 #include <ddk/protocol/i2c-lib.h>
12 #include <fbl/alloc_checker.h>
13 #include <fbl/auto_call.h>
14 #include <fbl/auto_lock.h>
15 #include <fbl/unique_ptr.h>
16 #include <hw/reg.h>
17 #include <stdint.h>
18 #include <threads.h>
19 #include <zircon/device/camera.h>
20 #include <zircon/types.h>
21
22 namespace camera {
23
24 namespace {
25
26 constexpr uint16_t kSensorId = 0x0227;
27 constexpr uint32_t kAGainPrecision = 12;
28 constexpr uint32_t kDGainPrecision = 8;
29 constexpr int32_t kLog2GainShift = 18;
30 constexpr int32_t kSensorExpNumber = 1;
31 constexpr uint32_t kMasterClock = 288000000;
32
33 } // namespace
34
InitPdev(zx_device_t * parent)35 zx_status_t Imx227Device::InitPdev(zx_device_t* parent) {
36 if (!pdev_.is_valid()) {
37 zxlogf(ERROR, "%s: ZX_PROTOCOL_PDEV not available\n", __FUNCTION__);
38 return ZX_ERR_NO_RESOURCES;
39 }
40
41 for (uint32_t i = 0; i < countof(gpios_); i++) {
42 std::optional<ddk::GpioProtocolClient> gpio;
43 gpio = pdev_.GetGpio(i);
44 if (!gpio) {
45 return ZX_ERR_NO_RESOURCES;
46 }
47 gpios_[i] = *gpio;
48 // Set the GPIO to output and set initial value to 0.
49 gpios_[i].ConfigOut(0);
50 }
51
52 // I2c for communicating with the sensor.
53 if (i2c_.is_valid()) {
54 return ZX_ERR_NO_RESOURCES;
55 }
56
57 // Clk for gating clocks for sensor.
58 if (clk_.is_valid()) {
59 return ZX_ERR_NO_RESOURCES;
60 }
61
62 // Mipi for init and de-init.
63 if (!mipi_.is_valid()) {
64 return ZX_ERR_NO_RESOURCES;
65 }
66
67 return ZX_OK;
68 }
69
ReadReg(uint16_t addr)70 uint8_t Imx227Device::ReadReg(uint16_t addr) {
71 // Convert the address to Big Endian format.
72 // The camera sensor expects in this format.
73 uint16_t buf = htobe16(addr);
74 uint8_t val = 0;
75 zx_status_t status = i2c_.WriteReadSync(reinterpret_cast<uint8_t*>(&buf), sizeof(buf),
76 &val, sizeof(val));
77 if (status != ZX_OK) {
78 zxlogf(ERROR, "Imx227Device: could not read reg addr: 0x%08x status: %d\n", addr, status);
79 return -1;
80 }
81 return val;
82 }
83
WriteReg(uint16_t addr,uint8_t val)84 void Imx227Device::WriteReg(uint16_t addr, uint8_t val) {
85 // Convert the address to Big Endian format.
86 // The camera sensor expects in this format.
87 // First two bytes are the address, third one is the value to be written.
88 uint8_t buf[3];
89 buf[1] = static_cast<uint8_t>(addr & 0xFF);
90 buf[0] = static_cast<uint8_t>((addr >> 8) & 0xFF);
91 buf[2] = val;
92
93 zx_status_t status = i2c_.WriteSync(buf, 3);
94 if (status != ZX_OK) {
95 zxlogf(ERROR, "Imx227Device: could not write reg addr/val: 0x%08x/0x%08x status: %d\n",
96 addr, val, status);
97 }
98 }
99
ValidateSensorID()100 bool Imx227Device::ValidateSensorID() {
101 uint16_t sensor_id = static_cast<uint16_t>((ReadReg(0x0016) << 8) | ReadReg(0x0017));
102 if (sensor_id != kSensorId) {
103 zxlogf(ERROR, "Imx227Device: Invalid sensor ID\n");
104 return false;
105 }
106 return true;
107 }
108
InitSensor(uint8_t idx)109 zx_status_t Imx227Device::InitSensor(uint8_t idx) {
110 if (idx >= countof(kSEQUENCE_TABLE)) {
111 return ZX_ERR_INVALID_ARGS;
112 }
113
114 const init_seq_fmt_t* sequence = kSEQUENCE_TABLE[idx];
115 bool init_command = true;
116
117 while (init_command) {
118 uint16_t address = sequence->address;
119 uint8_t value = sequence->value;
120
121 switch (address) {
122 case 0x0000: {
123 if (sequence->value == 0 && sequence->len == 0) {
124 init_command = false;
125 } else {
126 WriteReg(address, value);
127 }
128 break;
129 }
130 default:
131 WriteReg(address, value);
132 break;
133 }
134 sequence++;
135 }
136 return ZX_OK;
137 }
138
Init()139 zx_status_t Imx227Device::Init() {
140
141 // Power up sequence. Reference: Page 51- IMX227-0AQH5-C datasheet.
142 gpios_[VANA_ENABLE].ConfigOut(1);
143 zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
144
145 gpios_[VDIG_ENABLE].ConfigOut(1);
146 zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
147
148 // Enable 24M clock for sensor.
149 clk_.Enable(0);
150 zx_nanosleep(zx_deadline_after(ZX_MSEC(10)));
151
152 gpios_[CAM_SENSOR_RST].ConfigOut(0);
153 zx_nanosleep(zx_deadline_after(ZX_MSEC(50)));
154
155 // Get Sensor ID to validate initialization sequence.
156 if (!ValidateSensorID()) {
157 return ZX_ERR_INTERNAL;
158 }
159
160 // Initialize Sensor Context.
161 ctx_.seq_width = 1;
162 ctx_.streaming_flag = 0;
163 ctx_.again_old = 0;
164 ctx_.change_flag = 0;
165 ctx_.again_limit = 8 << kAGainPrecision;
166 ctx_.dgain_limit = 15 << kDGainPrecision;
167
168 // Initialize Sensor Parameters.
169 ctx_.param.again_accuracy = 1 << kLog2GainShift;
170 ctx_.param.sensor_exp_number = kSensorExpNumber;
171 ctx_.param.again_log2_max = 3 << kLog2GainShift;
172 ctx_.param.dgain_log2_max = 3 << kLog2GainShift;
173 ctx_.param.integration_time_apply_delay = 2;
174 ctx_.param.isp_exposure_channel_delay = 0;
175
176 return ZX_OK;
177 }
178
DeInit()179 void Imx227Device::DeInit() {
180 mipi_.DeInit();
181 }
182
GetInfo(fuchsia_hardware_camera_SensorInfo * out_info)183 zx_status_t Imx227Device::GetInfo(fuchsia_hardware_camera_SensorInfo* out_info) {
184 return ZX_ERR_NOT_SUPPORTED;
185 }
186
SetMode(uint8_t mode)187 zx_status_t Imx227Device::SetMode(uint8_t mode) {
188 // Get Sensor ID to see if sensor is initialized.
189 if (!ValidateSensorID()) {
190 return ZX_ERR_INTERNAL;
191 }
192
193 if (mode >= countof(supported_modes)) {
194 return ZX_ERR_INVALID_ARGS;
195 }
196
197 switch (supported_modes[mode].wdr_mode) {
198 case kWDR_MODE_LINEAR: {
199
200 InitSensor(supported_modes[mode].idx);
201
202 ctx_.again_delay = 0;
203 ctx_.dgain_delay = 0;
204 ctx_.param.integration_time_apply_delay = 2;
205 ctx_.param.isp_exposure_channel_delay = 0;
206 ctx_.hdr_flag = 0;
207 break;
208 }
209 // TODO(braval) : Support other modes.
210 default:
211 return ZX_ERR_NOT_SUPPORTED;
212 }
213
214 ctx_.param.active.width = supported_modes[mode].resolution.width;
215 ctx_.param.active.height = supported_modes[mode].resolution.height;
216 ctx_.HMAX = static_cast<uint16_t>(ReadReg(0x342) << 8 | ReadReg(0x343));
217 ctx_.VMAX = static_cast<uint16_t>(ReadReg(0x340) << 8 | ReadReg(0x341));
218 ctx_.int_max = 0x0A8C; // Max allowed for 30fps = 2782 (dec), 0x0A8E (hex)
219 ctx_.int_time_min = 1;
220 ctx_.int_time_limit = ctx_.int_max;
221 ctx_.param.total.height = ctx_.VMAX;
222 ctx_.param.total.width = ctx_.HMAX;
223 ctx_.param.pixels_per_line = ctx_.param.total.width;
224
225 uint32_t master_clock = kMasterClock;
226 ctx_.param.lines_per_second = master_clock / ctx_.HMAX;
227
228 ctx_.param.integration_time_min = ctx_.int_time_min;
229 ctx_.param.integration_time_limit = ctx_.int_time_limit;
230 ctx_.param.integration_time_max = ctx_.int_time_limit;
231 ctx_.param.integration_time_long_max = ctx_.int_time_limit;
232 ctx_.param.mode = mode;
233 ctx_.param.bayer = supported_modes[mode].bayer;
234 ctx_.wdr_mode = supported_modes[mode].wdr_mode;
235
236 mipi_info_t mipi_info;
237 mipi_adap_info_t adap_info;
238
239 mipi_info.lanes = supported_modes[mode].lanes;
240 mipi_info.ui_value = 1000 / supported_modes[mode].mbps;
241 if ((1000 % supported_modes[mode].mbps) != 0) {
242 mipi_info.ui_value += 1;
243 }
244
245 switch (supported_modes[mode].bits) {
246 case 10:
247 adap_info.format = IMAGE_FORMAT_AM_RAW10;
248 break;
249 case 12:
250 adap_info.format = IMAGE_FORMAT_AM_RAW12;
251 break;
252 default:
253 adap_info.format = IMAGE_FORMAT_AM_RAW10;
254 break;
255 }
256
257 adap_info.resolution.width = supported_modes[mode].resolution.width;
258 adap_info.resolution.height = supported_modes[mode].resolution.height;
259 adap_info.path = MIPI_PATH_PATH0;
260 adap_info.mode = MIPI_MODES_DDR_MODE;
261 return mipi_.Init(&mipi_info, &adap_info);
262 }
263
StartStreaming()264 void Imx227Device::StartStreaming() {
265 ctx_.streaming_flag = 1;
266 WriteReg(0x0100, 0x01);
267 }
268
StopStreaming()269 void Imx227Device::StopStreaming() {
270 ctx_.streaming_flag = 0;
271 WriteReg(0x0100, 0x00);
272 }
273
SetAnalogGain(int32_t gain)274 int32_t Imx227Device::SetAnalogGain(int32_t gain) {
275 return ZX_ERR_NOT_SUPPORTED;
276 }
277
SetDigitalGain(int32_t gain)278 int32_t Imx227Device::SetDigitalGain(int32_t gain) {
279 return ZX_ERR_NOT_SUPPORTED;
280 }
281
SetIntegrationTime(int32_t int_time,int32_t int_time_M,int32_t int_time_L)282 void Imx227Device::SetIntegrationTime(int32_t int_time,
283 int32_t int_time_M,
284 int32_t int_time_L) {
285 }
286
Update()287 void Imx227Device::Update() {
288 }
289
DdkIoctl(uint32_t op,const void * in_buf,size_t in_len,void * out_buf,size_t out_len,size_t * out_actual)290 zx_status_t Imx227Device::DdkIoctl(uint32_t op, const void* in_buf, size_t in_len,
291 void* out_buf, size_t out_len, size_t* out_actual) {
292 switch (op) {
293 case CAMERA_IOCTL_GET_SUPPORTED_MODES: {
294 if (out_len < sizeof(fuchsia_hardware_camera_SensorMode) * MAX_SUPPORTED_MODES) {
295 return ZX_ERR_BUFFER_TOO_SMALL;
296 }
297 memcpy(out_buf, &supported_modes, sizeof(supported_modes));
298 *out_actual = sizeof(supported_modes);
299 return ZX_OK;
300 }
301
302 default:
303 return ZX_ERR_NOT_SUPPORTED;
304 }
305 }
306
Init(void * ctx,fidl_txn_t * txn)307 static zx_status_t Init(void* ctx, fidl_txn_t* txn) {
308 auto& self = *static_cast<Imx227Device*>(ctx);
309 zx_status_t status = self.Init();
310 return fuchsia_hardware_camera_CameraSensorInit_reply(txn, status);
311 }
312
DeInit(void * ctx)313 static zx_status_t DeInit(void* ctx) {
314 auto& self = *static_cast<Imx227Device*>(ctx);
315 self.DeInit();
316 return ZX_OK;
317 }
318
SetMode(void * ctx,uint8_t mode,fidl_txn_t * txn)319 static zx_status_t SetMode(void* ctx, uint8_t mode, fidl_txn_t* txn) {
320 auto& self = *static_cast<Imx227Device*>(ctx);
321 zx_status_t status = self.SetMode(mode);
322 return fuchsia_hardware_camera_CameraSensorSetMode_reply(txn, status);
323 }
324
StartStreaming(void * ctx)325 static zx_status_t StartStreaming(void* ctx) {
326 auto& self = *static_cast<Imx227Device*>(ctx);
327 self.StartStreaming();
328 return ZX_OK;
329 }
330
StopStreaming(void * ctx)331 static zx_status_t StopStreaming(void* ctx) {
332 auto& self = *static_cast<Imx227Device*>(ctx);
333 self.StopStreaming();
334 return ZX_OK;
335 }
336
SetAnalogGain(void * ctx,int32_t gain,fidl_txn_t * txn)337 static zx_status_t SetAnalogGain(void* ctx, int32_t gain, fidl_txn_t* txn) {
338 auto& self = *static_cast<Imx227Device*>(ctx);
339 int32_t actual_gain = self.SetAnalogGain(gain);
340 return fuchsia_hardware_camera_CameraSensorSetAnalogGain_reply(txn, actual_gain);
341 }
342
SetDigitalGain(void * ctx,int32_t gain,fidl_txn_t * txn)343 static zx_status_t SetDigitalGain(void* ctx, int32_t gain, fidl_txn_t* txn) {
344 auto& self = *static_cast<Imx227Device*>(ctx);
345 int32_t actual_gain = self.SetDigitalGain(gain);
346 return fuchsia_hardware_camera_CameraSensorSetDigitalGain_reply(txn, actual_gain);
347 }
348
SetIntegrationTime(void * ctx,int32_t int_time,int32_t int_time_M,int32_t int_time_L)349 static zx_status_t SetIntegrationTime(void* ctx,
350 int32_t int_time,
351 int32_t int_time_M,
352 int32_t int_time_L) {
353 auto& self = *static_cast<Imx227Device*>(ctx);
354 self.SetIntegrationTime(int_time, int_time_M, int_time_L);
355 return ZX_OK;
356 }
357
Update(void * ctx)358 static zx_status_t Update(void* ctx) {
359 auto& self = *static_cast<Imx227Device*>(ctx);
360 self.Update();
361 return ZX_OK;
362 }
363
364 fuchsia_hardware_camera_CameraSensor_ops_t fidl_ops = {
365 .Init = Init,
366 .DeInit = DeInit,
367 .SetMode = SetMode,
368 .StartStreaming = StartStreaming,
369 .StopStreaming = StopStreaming,
370 .SetAnalogGain = SetAnalogGain,
371 .SetDigitalGain = SetDigitalGain,
372 .SetIntegrationTime = SetIntegrationTime,
373 .Update = Update,
374 };
375
DdkMessage(fidl_msg_t * msg,fidl_txn_t * txn)376 zx_status_t Imx227Device::DdkMessage(fidl_msg_t* msg, fidl_txn_t* txn) {
377 return fuchsia_hardware_camera_CameraSensor_dispatch(this, txn, msg, &fidl_ops);
378 }
379
Create(zx_device_t * parent)380 zx_status_t Imx227Device::Create(zx_device_t* parent) {
381 fbl::AllocChecker ac;
382 auto sensor_device = fbl::make_unique_checked<Imx227Device>(&ac, parent);
383 if (!ac.check()) {
384 return ZX_ERR_NO_MEMORY;
385 }
386
387 zx_status_t status = sensor_device->InitPdev(parent);
388 if (status != ZX_OK) {
389 return status;
390 }
391
392 status = sensor_device->DdkAdd("imx227");
393
394 // sensor_device intentionally leaked as it is now held by DevMgr.
395 __UNUSED auto ptr = sensor_device.release();
396
397 return status;
398 }
399
ShutDown()400 void Imx227Device::ShutDown() {
401 }
402
DdkUnbind()403 void Imx227Device::DdkUnbind() {
404 DdkRemove();
405 }
406
DdkRelease()407 void Imx227Device::DdkRelease() {
408 ShutDown();
409 delete this;
410 }
411
412 } // namespace camera
413
imx227_bind(void * ctx,zx_device_t * device)414 extern "C" zx_status_t imx227_bind(void* ctx, zx_device_t* device) {
415 return camera::Imx227Device::Create(device);
416 }
417