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 "aml-mipi.h"
6 #include "aml-mipi-regs.h"
7 #include <ddk/binding.h>
8 #include <ddk/debug.h>
9 #include <ddk/metadata.h>
10 #include <ddk/metadata/camera.h>
11 #include <fbl/alloc_checker.h>
12 #include <fbl/auto_call.h>
13 #include <fbl/unique_ptr.h>
14 #include <hw/reg.h>
15 #include <stdint.h>
16 #include <threads.h>
17 #include <zircon/types.h>
18
19 // NOTE: A lot of magic numbers, they come from vendor
20 // source code.
21
22 namespace camera {
23
24 namespace {
25
26 // MMIO Indexes.
27 constexpr uint32_t kCsiPhy0 = 0;
28 constexpr uint32_t kAphy0 = 1;
29 constexpr uint32_t kCsiHost0 = 2;
30 constexpr uint32_t kMipiAdap = 3;
31 constexpr uint32_t kHiu = 4;
32 constexpr uint32_t kPowerDomain = 5;
33 constexpr uint32_t kMemoryDomain = 6;
34 constexpr uint32_t kReset = 7;
35
36 // CLK Shifts & Masks
37 constexpr uint32_t kClkMuxMask = 0xfff;
38 constexpr uint32_t kClkEnableShift = 8;
39
40 } // namespace
41
IspHWReset(bool reset)42 void AmlMipiDevice::IspHWReset(bool reset) {
43 if (reset) {
44 reset_mmio_->ClearBits32(1 << 1, RESET4_LEVEL);
45 } else {
46 reset_mmio_->SetBits32(1 << 1, RESET4_LEVEL);
47 }
48 }
49
PowerUpIsp()50 void AmlMipiDevice::PowerUpIsp() {
51 // set bit[18-19]=0
52 power_mmio_->ClearBits32(1 << 18 | 1 << 19, AO_RTI_GEN_PWR_SLEEP0);
53 zx_nanosleep(zx_deadline_after(ZX_MSEC(5)));
54
55 // set bit[18-19]=0
56 power_mmio_->ClearBits32(1 << 18 | 1 << 19, AO_RTI_GEN_PWR_ISO0);
57
58 // MEM_PD_REG0 set 0
59 memory_pd_mmio_->Write32(0, HHI_ISP_MEM_PD_REG0);
60 // MEM_PD_REG1 set 0
61 memory_pd_mmio_->Write32(0, HHI_ISP_MEM_PD_REG1);
62
63 hiu_mmio_->Write32(0x5b446585, HHI_CSI_PHY_CNTL0);
64 hiu_mmio_->Write32(0x803f4321, HHI_CSI_PHY_CNTL1);
65 }
66
InitMipiClock()67 void AmlMipiDevice::InitMipiClock() {
68 // clear existing values
69 hiu_mmio_->ClearBits32(kClkMuxMask, HHI_MIPI_ISP_CLK_CNTL);
70 // set the divisor = 1 (writing (1-1) to div field)
71 // source for the unused mux = S905D2_FCLK_DIV3 = 3 // 666.7 MHz
72 hiu_mmio_->SetBits32(((1 << kClkEnableShift) | 4 << 9),
73 HHI_MIPI_ISP_CLK_CNTL);
74
75 // clear existing values
76 hiu_mmio_->ClearBits32(kClkMuxMask, HHI_MIPI_CSI_PHY_CLK_CNTL);
77 // set the divisor = 2 (writing (2-1) to div field)
78 // source for the unused mux = S905D2_FCLK_DIV5 = 6 // 400 MHz
79 hiu_mmio_->SetBits32(((1 << kClkEnableShift) | 6 << 9 | 1),
80 HHI_MIPI_CSI_PHY_CLK_CNTL);
81
82 zx_nanosleep(zx_deadline_after(ZX_USEC(10)));
83 }
84
InitPdev(zx_device_t * parent)85 zx_status_t AmlMipiDevice::InitPdev(zx_device_t* parent) {
86 zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &pdev_);
87 if (status != ZX_OK) {
88 zxlogf(ERROR, "%s: ZX_PROTOCOL_PDEV not available %d \n", __FUNCTION__, status);
89 return status;
90 }
91
92 mmio_buffer_t mmio;
93 status = pdev_map_mmio_buffer2(&pdev_,
94 kCsiPhy0, ZX_CACHE_POLICY_UNCACHED_DEVICE,
95 &mmio);
96 if (status != ZX_OK) {
97 zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
98 return status;
99 }
100 csi_phy0_mmio_ = fbl::make_unique<ddk::MmioBuffer>(mmio);
101
102 status = pdev_map_mmio_buffer2(&pdev_, kAphy0, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
103 if (status != ZX_OK) {
104 zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
105 return status;
106 }
107 aphy0_mmio_ = fbl::make_unique<ddk::MmioBuffer>(mmio);
108
109 status = pdev_map_mmio_buffer2(&pdev_, kCsiHost0, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
110 if (status != ZX_OK) {
111 zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
112 return status;
113 }
114 csi_host0_mmio_ = fbl::make_unique<ddk::MmioBuffer>(mmio);
115
116 status = pdev_map_mmio_buffer2(&pdev_, kMipiAdap, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
117 if (status != ZX_OK) {
118 zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
119 return status;
120 }
121 mipi_adap_mmio_ = fbl::make_unique<ddk::MmioBuffer>(mmio);
122
123 status = pdev_map_mmio_buffer2(&pdev_, kHiu, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
124 if (status != ZX_OK) {
125 zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
126 return status;
127 }
128 hiu_mmio_ = fbl::make_unique<ddk::MmioBuffer>(mmio);
129
130 status = pdev_map_mmio_buffer2(&pdev_, kPowerDomain, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
131 if (status != ZX_OK) {
132 zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
133 return status;
134 }
135 power_mmio_ = fbl::make_unique<ddk::MmioBuffer>(mmio);
136
137 status = pdev_map_mmio_buffer2(&pdev_, kMemoryDomain, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
138 if (status != ZX_OK) {
139 zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
140 return status;
141 }
142 memory_pd_mmio_ = fbl::make_unique<ddk::MmioBuffer>(mmio);
143
144 status = pdev_map_mmio_buffer2(&pdev_, kReset, ZX_CACHE_POLICY_UNCACHED_DEVICE, &mmio);
145 if (status != ZX_OK) {
146 zxlogf(ERROR, "%s: pdev_map_mmio_buffer2 failed %d\n", __FUNCTION__, status);
147 return status;
148 }
149 reset_mmio_ = fbl::make_unique<ddk::MmioBuffer>(mmio);
150
151 // Get our bti.
152 status = pdev_get_bti(&pdev_, 0, bti_.reset_and_get_address());
153 if (status != ZX_OK) {
154 zxlogf(ERROR, "%s: could not obtain bti: %d\n", __FUNCTION__, status);
155 return status;
156 }
157
158 // Get adapter interrupt.
159 status = pdev_map_interrupt(&pdev_, 0, adap_irq_.reset_and_get_address());
160 if (status != ZX_OK) {
161 zxlogf(ERROR, "%s: could not obtain adapter interrupt %d\n", __FUNCTION__, status);
162 return status;
163 }
164
165 return status;
166 }
167
MipiPhyReset()168 void AmlMipiDevice::MipiPhyReset() {
169 uint32_t data32 = 0x1f; //disable lanes digital clock
170 data32 |= 0x1 << 31; //soft reset bit
171 csi_phy0_mmio_->Write32(data32, MIPI_PHY_CTRL);
172 }
173
MipiCsi2Reset()174 void AmlMipiDevice::MipiCsi2Reset() {
175 csi_host0_mmio_->Write32(0, MIPI_CSI_PHY_SHUTDOWNZ); // enable power
176 csi_host0_mmio_->Write32(0, MIPI_CSI_DPHY_RSTZ); // release DPHY reset
177 csi_host0_mmio_->Write32(0, MIPI_CSI_CSI2_RESETN); // csi2 reset
178 }
179
MipiPhyInit(const mipi_info_t * info)180 void AmlMipiDevice::MipiPhyInit(const mipi_info_t* info) {
181 if (info->ui_value <= 1) {
182 aphy0_mmio_->Write32(0x0b440585, HI_CSI_PHY_CNTL0);
183 } else {
184 aphy0_mmio_->Write32(0x0b440581, HI_CSI_PHY_CNTL0);
185 }
186
187 aphy0_mmio_->Write32(0x803f0000, HI_CSI_PHY_CNTL1);
188 aphy0_mmio_->Write32(0x02, HI_CSI_PHY_CNTL3);
189
190 // 3d8 :continue mode
191 csi_phy0_mmio_->Write32(0x3d8, MIPI_PHY_CLK_LANE_CTRL);
192 // clck miss = 50 ns --(x< 60 ns)
193 csi_phy0_mmio_->Write32(0x9, MIPI_PHY_TCLK_MISS);
194 // clck settle = 160 ns --(95ns< x < 300 ns)
195 csi_phy0_mmio_->Write32(0x1f, MIPI_PHY_TCLK_SETTLE);
196 // hs exit = 160 ns --(x>100ns)
197 csi_phy0_mmio_->Write32(0x1f, MIPI_PHY_THS_EXIT);
198 // hs skip = 55 ns --(40ns<x<55ns+4*UI)
199 csi_phy0_mmio_->Write32(0xa, MIPI_PHY_THS_SKIP);
200
201 // No documentation for this regisgter.
202 // hs settle = 160 ns --(85 ns + 6*UI<x<145 ns + 10*UI)
203 uint32_t settle = ((85 + 145 + (16 * info->ui_value)) / 2) / 5;
204 csi_phy0_mmio_->Write32(settle, MIPI_PHY_THS_SETTLE);
205
206 csi_phy0_mmio_->Write32(0x4e20, MIPI_PHY_TINIT); // >100us
207 csi_phy0_mmio_->Write32(0x100, MIPI_PHY_TMBIAS);
208 csi_phy0_mmio_->Write32(0x1000, MIPI_PHY_TULPS_C);
209 csi_phy0_mmio_->Write32(0x100, MIPI_PHY_TULPS_S);
210 csi_phy0_mmio_->Write32(0x0c, MIPI_PHY_TLP_EN_W);
211 csi_phy0_mmio_->Write32(0x100, MIPI_PHY_TLPOK);
212 csi_phy0_mmio_->Write32(0x400000, MIPI_PHY_TWD_INIT);
213 csi_phy0_mmio_->Write32(0x400000, MIPI_PHY_TWD_HS);
214 csi_phy0_mmio_->Write32(0x0, MIPI_PHY_DATA_LANE_CTRL);
215 // enable data lanes pipe line and hs sync bit err.
216 csi_phy0_mmio_->Write32((0x3 | (0x1f << 2) | (0x3 << 7)), MIPI_PHY_DATA_LANE_CTRL1);
217 csi_phy0_mmio_->Write32(0x00000123, MIPI_PHY_MUX_CTRL0);
218 csi_phy0_mmio_->Write32(0x00000123, MIPI_PHY_MUX_CTRL1);
219
220 // NOTE: Possible bug in reference code. Leaving it here for future reference.
221 // uint32_t data32 = ((~(info->channel)) & 0xf) | (0 << 4); //enable lanes digital clock
222 // data32 |= ((0x10 | info->channel) << 5); //mipi_chpu to analog
223 csi_phy0_mmio_->Write32(0, MIPI_PHY_CTRL);
224 }
225
MipiCsi2Init(const mipi_info_t * info)226 void AmlMipiDevice::MipiCsi2Init(const mipi_info_t* info) {
227 // csi2 reset
228 csi_host0_mmio_->Write32(0, MIPI_CSI_CSI2_RESETN);
229 // release csi2 reset
230 csi_host0_mmio_->Write32(0xffffffff, MIPI_CSI_CSI2_RESETN);
231 // release DPHY reset
232 csi_host0_mmio_->Write32(0xffffffff, MIPI_CSI_DPHY_RSTZ);
233 //set lanes
234 csi_host0_mmio_->Write32((info->lanes - 1) & 3, MIPI_CSI_N_LANES);
235 // enable power
236 csi_host0_mmio_->Write32(0xffffffff, MIPI_CSI_PHY_SHUTDOWNZ);
237 }
238
239 // static
MipiCsiInit(void * ctx,const mipi_info_t * mipi_info,const mipi_adap_info_t * adap_info)240 zx_status_t AmlMipiDevice::MipiCsiInit(void* ctx,
241 const mipi_info_t* mipi_info,
242 const mipi_adap_info_t* adap_info) {
243 auto& self = *static_cast<AmlMipiDevice*>(ctx);
244
245 // The ISP and MIPI module is in same power domain.
246 // So if we don't call the power sequence of ISP, the mipi module
247 // won't work and it will block accesses to the mipi register block.
248 self.PowerUpIsp();
249
250 // Setup MIPI CSI PHY CLK to 200MHz.
251 // Setup MIPI ISP CLK to 667MHz.
252 self.InitMipiClock();
253
254 self.IspHWReset(true);
255 self.IspHWReset(false);
256
257 // Initialize the PHY.
258 self.MipiPhyInit(mipi_info);
259 // Initialize the CSI Host.
260 self.MipiCsi2Init(mipi_info);
261
262 // Initialize the MIPI Adapter.
263 zx_status_t status = self.MipiAdapInit(adap_info);
264 if (status != ZX_OK) {
265 zxlogf(ERROR, "%s: MipiAdapInit failed %d\n", __FUNCTION__, status);
266 return status;
267 }
268
269 // Start the MIPI Adapter.
270 self.MipiAdapStart(adap_info);
271 return status;
272 }
273
274 // static
MipiCsiDeInit(void * ctx)275 zx_status_t AmlMipiDevice::MipiCsiDeInit(void* ctx) {
276 auto& self = *static_cast<AmlMipiDevice*>(ctx);
277 self.MipiPhyReset();
278 self.MipiCsi2Reset();
279 self.MipiAdapReset();
280 return ZX_OK;
281 }
282
ShutDown()283 void AmlMipiDevice::ShutDown() {
284 MipiCsiDeInit(this);
285 csi_phy0_mmio_.reset();
286 aphy0_mmio_.reset();
287 csi_host0_mmio_.reset();
288 mipi_adap_mmio_.reset();
289 hiu_mmio_.reset();
290 power_mmio_.reset();
291 memory_pd_mmio_.reset();
292 reset_mmio_.reset();
293 }
294
DdkUnbind(void * ctx)295 static void DdkUnbind(void* ctx) {
296 auto& self = *static_cast<AmlMipiDevice*>(ctx);
297 device_remove(self.device_);
298 }
299
DdkRelease(void * ctx)300 static void DdkRelease(void* ctx) {
301 auto& self = *static_cast<AmlMipiDevice*>(ctx);
302 self.ShutDown();
303 delete &self;
304 }
305
306 static mipi_csi_protocol_ops_t proto_ops = {
307 .init = AmlMipiDevice::MipiCsiInit,
308 .de_init = AmlMipiDevice::MipiCsiDeInit,
309 };
310
__anonf74b7e900202() 311 static zx_protocol_device_t mipi_device_ops = []() {
312 zx_protocol_device_t result;
313
314 result.version = DEVICE_OPS_VERSION;
315 result.unbind = &DdkUnbind;
316 result.release = &DdkRelease;
317 return result;
318 }();
319
__anonf74b7e900302() 320 static device_add_args_t mipi_dev_args = []() {
321 device_add_args_t result;
322
323 result.version = DEVICE_ADD_ARGS_VERSION;
324 result.name = "aml-mipi";
325 result.ops = &mipi_device_ops;
326 result.proto_id = ZX_PROTOCOL_MIPI_CSI;
327 result.proto_ops = &proto_ops;
328 return result;
329 }();
330
331 // static
Create(zx_device_t * parent)332 zx_status_t AmlMipiDevice::Create(zx_device_t* parent) {
333 fbl::AllocChecker ac;
334 auto mipi_device = fbl::make_unique_checked<AmlMipiDevice>(&ac);
335 if (!ac.check()) {
336 return ZX_ERR_NO_MEMORY;
337 }
338
339 zx_status_t status = mipi_device->InitPdev(parent);
340 if (status != ZX_OK) {
341 return status;
342 }
343 // Populate board specific information
344 camera_sensor_t sensor_info;
345 size_t actual;
346 status = device_get_metadata(parent, DEVICE_METADATA_PRIVATE, &sensor_info,
347 sizeof(camera_sensor_t), &actual);
348 if (status != ZX_OK || actual != sizeof(camera_sensor_t)) {
349 zxlogf(ERROR, "aml-mipi: Could not get Sensor Info metadata %d\n", status);
350 return status;
351 }
352
353 static zx_device_prop_t props[] = {
354 {BIND_PLATFORM_DEV_VID, 0, sensor_info.vid},
355 {BIND_PLATFORM_DEV_PID, 0, sensor_info.pid},
356 {BIND_PLATFORM_DEV_DID, 0, sensor_info.did},
357 };
358
359 mipi_dev_args.props = props;
360 mipi_dev_args.prop_count = countof(props);
361 mipi_dev_args.ctx = mipi_device.get();
362
363 status = pdev_device_add(&mipi_device->pdev_, 0, &mipi_dev_args, &mipi_device->device_);
364 if (status != ZX_OK) {
365 zxlogf(ERROR, "aml-mipi driver failed to get added\n");
366 return status;
367 } else {
368 zxlogf(INFO, "aml-mipi driver added\n");
369 }
370
371 // mipi_device intentionally leaked as it is now held by DevMgr.
372 __UNUSED auto ptr = mipi_device.release();
373
374 return status;
375 }
376
~AmlMipiDevice()377 AmlMipiDevice::~AmlMipiDevice() {
378 adap_irq_.destroy();
379 running_.store(false);
380 thrd_join(irq_thread_, NULL);
381 }
382
383 } // namespace camera
384
aml_mipi_bind(void * ctx,zx_device_t * device)385 extern "C" zx_status_t aml_mipi_bind(void* ctx, zx_device_t* device) {
386 return camera::AmlMipiDevice::Create(device);
387 }
388