.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
---|
1 | 2 | /* |
---|
2 | 3 | * Driver for Analog Devices ADV748X HDMI receiver with AFE |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2017 Renesas Electronics Corp. |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify it |
---|
7 | | - * under the terms of the GNU General Public License as published by the |
---|
8 | | - * Free Software Foundation; either version 2 of the License, or (at your |
---|
9 | | - * option) any later version. |
---|
10 | 6 | * |
---|
11 | 7 | * Authors: |
---|
12 | 8 | * Koji Matsuoka <koji.matsuoka.xm@renesas.com> |
---|
.. | .. |
---|
27 | 23 | #include <media/v4l2-ctrls.h> |
---|
28 | 24 | #include <media/v4l2-device.h> |
---|
29 | 25 | #include <media/v4l2-dv-timings.h> |
---|
| 26 | +#include <media/v4l2-fwnode.h> |
---|
30 | 27 | #include <media/v4l2-ioctl.h> |
---|
31 | 28 | |
---|
32 | 29 | #include "adv748x.h" |
---|
.. | .. |
---|
128 | 125 | return regmap_write(state->regmap[page], reg, value); |
---|
129 | 126 | } |
---|
130 | 127 | |
---|
| 128 | +static int adv748x_write_check(struct adv748x_state *state, u8 page, u8 reg, |
---|
| 129 | + u8 value, int *error) |
---|
| 130 | +{ |
---|
| 131 | + if (*error) |
---|
| 132 | + return *error; |
---|
| 133 | + |
---|
| 134 | + *error = adv748x_write(state, page, reg, value); |
---|
| 135 | + return *error; |
---|
| 136 | +} |
---|
| 137 | + |
---|
131 | 138 | /* adv748x_write_block(): Write raw data with a maximum of I2C_SMBUS_BLOCK_MAX |
---|
132 | 139 | * size to one or more registers. |
---|
133 | 140 | * |
---|
.. | .. |
---|
176 | 183 | int ret; |
---|
177 | 184 | |
---|
178 | 185 | for (i = ADV748X_PAGE_DPLL; i < ADV748X_PAGE_MAX; ++i) { |
---|
179 | | - state->i2c_clients[i] = i2c_new_secondary_device( |
---|
| 186 | + state->i2c_clients[i] = i2c_new_ancillary_device( |
---|
180 | 187 | state->client, |
---|
181 | 188 | adv748x_default_addresses[i].name, |
---|
182 | 189 | adv748x_default_addresses[i].default_addr); |
---|
183 | 190 | |
---|
184 | | - if (state->i2c_clients[i] == NULL) { |
---|
| 191 | + if (IS_ERR(state->i2c_clients[i])) { |
---|
185 | 192 | adv_err(state, "failed to create i2c client %u\n", i); |
---|
186 | | - return -ENOMEM; |
---|
| 193 | + return PTR_ERR(state->i2c_clients[i]); |
---|
187 | 194 | } |
---|
188 | 195 | |
---|
189 | 196 | ret = adv748x_configure_regmap(state, i); |
---|
.. | .. |
---|
211 | 218 | { |
---|
212 | 219 | int ret; |
---|
213 | 220 | |
---|
214 | | - while (regs->page != ADV748X_PAGE_EOR) { |
---|
215 | | - if (regs->page == ADV748X_PAGE_WAIT) { |
---|
216 | | - msleep(regs->value); |
---|
217 | | - } else { |
---|
218 | | - ret = adv748x_write(state, regs->page, regs->reg, |
---|
219 | | - regs->value); |
---|
220 | | - if (ret < 0) { |
---|
221 | | - adv_err(state, |
---|
222 | | - "Error regs page: 0x%02x reg: 0x%02x\n", |
---|
223 | | - regs->page, regs->reg); |
---|
224 | | - return ret; |
---|
225 | | - } |
---|
| 221 | + for (; regs->page != ADV748X_PAGE_EOR; regs++) { |
---|
| 222 | + ret = adv748x_write(state, regs->page, regs->reg, regs->value); |
---|
| 223 | + if (ret < 0) { |
---|
| 224 | + adv_err(state, "Error regs page: 0x%02x reg: 0x%02x\n", |
---|
| 225 | + regs->page, regs->reg); |
---|
| 226 | + return ret; |
---|
226 | 227 | } |
---|
227 | | - regs++; |
---|
228 | 228 | } |
---|
229 | 229 | |
---|
230 | 230 | return 0; |
---|
.. | .. |
---|
234 | 234 | * TXA and TXB |
---|
235 | 235 | */ |
---|
236 | 236 | |
---|
237 | | -static const struct adv748x_reg_value adv748x_power_up_txa_4lane[] = { |
---|
238 | | - |
---|
239 | | - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ |
---|
240 | | - {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ |
---|
241 | | - |
---|
242 | | - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ |
---|
243 | | - {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */ |
---|
244 | | - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ |
---|
245 | | - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ |
---|
246 | | - {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */ |
---|
247 | | - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ |
---|
248 | | - {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */ |
---|
249 | | - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ |
---|
250 | | - {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */ |
---|
251 | | - |
---|
252 | | - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ |
---|
253 | | -}; |
---|
254 | | - |
---|
255 | | -static const struct adv748x_reg_value adv748x_power_down_txa_4lane[] = { |
---|
256 | | - |
---|
257 | | - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ |
---|
258 | | - {ADV748X_PAGE_TXA, 0x1e, 0x00}, /* ADI Required Write */ |
---|
259 | | - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ |
---|
260 | | - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ |
---|
261 | | - {ADV748X_PAGE_TXA, 0xc1, 0x3b}, /* ADI Required Write */ |
---|
262 | | - |
---|
263 | | - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ |
---|
264 | | -}; |
---|
265 | | - |
---|
266 | | -static const struct adv748x_reg_value adv748x_power_up_txb_1lane[] = { |
---|
267 | | - |
---|
268 | | - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ |
---|
269 | | - {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ |
---|
270 | | - |
---|
271 | | - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ |
---|
272 | | - {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */ |
---|
273 | | - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ |
---|
274 | | - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ |
---|
275 | | - {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */ |
---|
276 | | - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ |
---|
277 | | - {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */ |
---|
278 | | - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ |
---|
279 | | - {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */ |
---|
280 | | - |
---|
281 | | - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ |
---|
282 | | -}; |
---|
283 | | - |
---|
284 | | -static const struct adv748x_reg_value adv748x_power_down_txb_1lane[] = { |
---|
285 | | - |
---|
286 | | - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ |
---|
287 | | - {ADV748X_PAGE_TXB, 0x1e, 0x00}, /* ADI Required Write */ |
---|
288 | | - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 4-lane MIPI */ |
---|
289 | | - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ |
---|
290 | | - {ADV748X_PAGE_TXB, 0xc1, 0x3b}, /* ADI Required Write */ |
---|
291 | | - |
---|
292 | | - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ |
---|
293 | | -}; |
---|
294 | | - |
---|
295 | | -int adv748x_txa_power(struct adv748x_state *state, bool on) |
---|
| 237 | +static int adv748x_power_up_tx(struct adv748x_csi2 *tx) |
---|
296 | 238 | { |
---|
297 | | - int val; |
---|
| 239 | + struct adv748x_state *state = tx->state; |
---|
| 240 | + u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB; |
---|
| 241 | + int ret = 0; |
---|
298 | 242 | |
---|
299 | | - val = txa_read(state, ADV748X_CSI_FS_AS_LS); |
---|
300 | | - if (val < 0) |
---|
301 | | - return val; |
---|
| 243 | + /* Enable n-lane MIPI */ |
---|
| 244 | + adv748x_write_check(state, page, 0x00, 0x80 | tx->active_lanes, &ret); |
---|
302 | 245 | |
---|
303 | | - /* |
---|
304 | | - * This test against BIT(6) is not documented by the datasheet, but was |
---|
305 | | - * specified in the downstream driver. |
---|
306 | | - * Track with a WARN_ONCE to determine if it is ever set by HW. |
---|
307 | | - */ |
---|
308 | | - WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN), |
---|
309 | | - "Enabling with unknown bit set"); |
---|
| 246 | + /* Set Auto DPHY Timing */ |
---|
| 247 | + adv748x_write_check(state, page, 0x00, 0xa0 | tx->active_lanes, &ret); |
---|
310 | 248 | |
---|
311 | | - if (on) |
---|
312 | | - return adv748x_write_regs(state, adv748x_power_up_txa_4lane); |
---|
| 249 | + /* ADI Required Write */ |
---|
| 250 | + if (tx->src == &state->hdmi.sd) { |
---|
| 251 | + adv748x_write_check(state, page, 0xdb, 0x10, &ret); |
---|
| 252 | + adv748x_write_check(state, page, 0xd6, 0x07, &ret); |
---|
| 253 | + } else { |
---|
| 254 | + adv748x_write_check(state, page, 0xd2, 0x40, &ret); |
---|
| 255 | + } |
---|
313 | 256 | |
---|
314 | | - return adv748x_write_regs(state, adv748x_power_down_txa_4lane); |
---|
| 257 | + adv748x_write_check(state, page, 0xc4, 0x0a, &ret); |
---|
| 258 | + adv748x_write_check(state, page, 0x71, 0x33, &ret); |
---|
| 259 | + adv748x_write_check(state, page, 0x72, 0x11, &ret); |
---|
| 260 | + |
---|
| 261 | + /* i2c_dphy_pwdn - 1'b0 */ |
---|
| 262 | + adv748x_write_check(state, page, 0xf0, 0x00, &ret); |
---|
| 263 | + |
---|
| 264 | + /* ADI Required Writes*/ |
---|
| 265 | + adv748x_write_check(state, page, 0x31, 0x82, &ret); |
---|
| 266 | + adv748x_write_check(state, page, 0x1e, 0x40, &ret); |
---|
| 267 | + |
---|
| 268 | + /* i2c_mipi_pll_en - 1'b1 */ |
---|
| 269 | + adv748x_write_check(state, page, 0xda, 0x01, &ret); |
---|
| 270 | + usleep_range(2000, 2500); |
---|
| 271 | + |
---|
| 272 | + /* Power-up CSI-TX */ |
---|
| 273 | + adv748x_write_check(state, page, 0x00, 0x20 | tx->active_lanes, &ret); |
---|
| 274 | + usleep_range(1000, 1500); |
---|
| 275 | + |
---|
| 276 | + /* ADI Required Writes */ |
---|
| 277 | + adv748x_write_check(state, page, 0xc1, 0x2b, &ret); |
---|
| 278 | + usleep_range(1000, 1500); |
---|
| 279 | + adv748x_write_check(state, page, 0x31, 0x80, &ret); |
---|
| 280 | + |
---|
| 281 | + return ret; |
---|
315 | 282 | } |
---|
316 | 283 | |
---|
317 | | -int adv748x_txb_power(struct adv748x_state *state, bool on) |
---|
| 284 | +static int adv748x_power_down_tx(struct adv748x_csi2 *tx) |
---|
| 285 | +{ |
---|
| 286 | + struct adv748x_state *state = tx->state; |
---|
| 287 | + u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB; |
---|
| 288 | + int ret = 0; |
---|
| 289 | + |
---|
| 290 | + /* ADI Required Writes */ |
---|
| 291 | + adv748x_write_check(state, page, 0x31, 0x82, &ret); |
---|
| 292 | + adv748x_write_check(state, page, 0x1e, 0x00, &ret); |
---|
| 293 | + |
---|
| 294 | + /* Enable n-lane MIPI */ |
---|
| 295 | + adv748x_write_check(state, page, 0x00, 0x80 | tx->active_lanes, &ret); |
---|
| 296 | + |
---|
| 297 | + /* i2c_mipi_pll_en - 1'b1 */ |
---|
| 298 | + adv748x_write_check(state, page, 0xda, 0x01, &ret); |
---|
| 299 | + |
---|
| 300 | + /* ADI Required Write */ |
---|
| 301 | + adv748x_write_check(state, page, 0xc1, 0x3b, &ret); |
---|
| 302 | + |
---|
| 303 | + return ret; |
---|
| 304 | +} |
---|
| 305 | + |
---|
| 306 | +int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) |
---|
318 | 307 | { |
---|
319 | 308 | int val; |
---|
320 | 309 | |
---|
321 | | - val = txb_read(state, ADV748X_CSI_FS_AS_LS); |
---|
| 310 | + if (!is_tx_enabled(tx)) |
---|
| 311 | + return 0; |
---|
| 312 | + |
---|
| 313 | + val = tx_read(tx, ADV748X_CSI_FS_AS_LS); |
---|
322 | 314 | if (val < 0) |
---|
323 | 315 | return val; |
---|
324 | 316 | |
---|
.. | .. |
---|
330 | 322 | WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN), |
---|
331 | 323 | "Enabling with unknown bit set"); |
---|
332 | 324 | |
---|
333 | | - if (on) |
---|
334 | | - return adv748x_write_regs(state, adv748x_power_up_txb_1lane); |
---|
335 | | - |
---|
336 | | - return adv748x_write_regs(state, adv748x_power_down_txb_1lane); |
---|
| 325 | + return on ? adv748x_power_up_tx(tx) : adv748x_power_down_tx(tx); |
---|
337 | 326 | } |
---|
338 | 327 | |
---|
339 | 328 | /* ----------------------------------------------------------------------------- |
---|
340 | 329 | * Media Operations |
---|
341 | 330 | */ |
---|
| 331 | +static int adv748x_link_setup(struct media_entity *entity, |
---|
| 332 | + const struct media_pad *local, |
---|
| 333 | + const struct media_pad *remote, u32 flags) |
---|
| 334 | +{ |
---|
| 335 | + struct v4l2_subdev *rsd = media_entity_to_v4l2_subdev(remote->entity); |
---|
| 336 | + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); |
---|
| 337 | + struct adv748x_state *state = v4l2_get_subdevdata(sd); |
---|
| 338 | + struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); |
---|
| 339 | + bool enable = flags & MEDIA_LNK_FL_ENABLED; |
---|
| 340 | + u8 io10_mask = ADV748X_IO_10_CSI1_EN | |
---|
| 341 | + ADV748X_IO_10_CSI4_EN | |
---|
| 342 | + ADV748X_IO_10_CSI4_IN_SEL_AFE; |
---|
| 343 | + u8 io10 = 0; |
---|
| 344 | + |
---|
| 345 | + /* Refuse to enable multiple links to the same TX at the same time. */ |
---|
| 346 | + if (enable && tx->src) |
---|
| 347 | + return -EINVAL; |
---|
| 348 | + |
---|
| 349 | + /* Set or clear the source (HDMI or AFE) and the current TX. */ |
---|
| 350 | + if (rsd == &state->afe.sd) |
---|
| 351 | + state->afe.tx = enable ? tx : NULL; |
---|
| 352 | + else |
---|
| 353 | + state->hdmi.tx = enable ? tx : NULL; |
---|
| 354 | + |
---|
| 355 | + tx->src = enable ? rsd : NULL; |
---|
| 356 | + |
---|
| 357 | + if (state->afe.tx) { |
---|
| 358 | + /* AFE Requires TXA enabled, even when output to TXB */ |
---|
| 359 | + io10 |= ADV748X_IO_10_CSI4_EN; |
---|
| 360 | + if (is_txa(tx)) { |
---|
| 361 | + /* |
---|
| 362 | + * Output from the SD-core (480i and 576i) from the TXA |
---|
| 363 | + * interface requires reducing the number of enabled |
---|
| 364 | + * data lanes in order to guarantee a valid link |
---|
| 365 | + * frequency. |
---|
| 366 | + */ |
---|
| 367 | + tx->active_lanes = min(tx->num_lanes, 2U); |
---|
| 368 | + io10 |= ADV748X_IO_10_CSI4_IN_SEL_AFE; |
---|
| 369 | + } else { |
---|
| 370 | + /* TXB has a single data lane, no need to adjust. */ |
---|
| 371 | + io10 |= ADV748X_IO_10_CSI1_EN; |
---|
| 372 | + } |
---|
| 373 | + } |
---|
| 374 | + |
---|
| 375 | + if (state->hdmi.tx) { |
---|
| 376 | + /* |
---|
| 377 | + * Restore the number of active lanes, in case we have gone |
---|
| 378 | + * through an AFE->TXA streaming sessions. |
---|
| 379 | + */ |
---|
| 380 | + tx->active_lanes = tx->num_lanes; |
---|
| 381 | + io10 |= ADV748X_IO_10_CSI4_EN; |
---|
| 382 | + } |
---|
| 383 | + |
---|
| 384 | + return io_clrset(state, ADV748X_IO_10, io10_mask, io10); |
---|
| 385 | +} |
---|
| 386 | + |
---|
| 387 | +static const struct media_entity_operations adv748x_tx_media_ops = { |
---|
| 388 | + .link_setup = adv748x_link_setup, |
---|
| 389 | + .link_validate = v4l2_subdev_link_validate, |
---|
| 390 | +}; |
---|
342 | 391 | |
---|
343 | 392 | static const struct media_entity_operations adv748x_media_ops = { |
---|
344 | 393 | .link_validate = v4l2_subdev_link_validate, |
---|
.. | .. |
---|
348 | 397 | * HW setup |
---|
349 | 398 | */ |
---|
350 | 399 | |
---|
351 | | -static const struct adv748x_reg_value adv748x_sw_reset[] = { |
---|
352 | | - |
---|
353 | | - {ADV748X_PAGE_IO, 0xff, 0xff}, /* SW reset */ |
---|
354 | | - {ADV748X_PAGE_WAIT, 0x00, 0x05},/* delay 5 */ |
---|
355 | | - {ADV748X_PAGE_IO, 0x01, 0x76}, /* ADI Required Write */ |
---|
356 | | - {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */ |
---|
357 | | - {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ |
---|
358 | | -}; |
---|
359 | | - |
---|
360 | | -/* Supported Formats For Script Below */ |
---|
361 | | -/* - 01-29 HDMI to MIPI TxA CSI 4-Lane - RGB888: */ |
---|
362 | | -static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { |
---|
| 400 | +/* Initialize CP Core with RGB888 format. */ |
---|
| 401 | +static const struct adv748x_reg_value adv748x_init_hdmi[] = { |
---|
363 | 402 | /* Disable chip powerdown & Enable HDMI Rx block */ |
---|
364 | 403 | {ADV748X_PAGE_IO, 0x00, 0x40}, |
---|
365 | 404 | |
---|
.. | .. |
---|
399 | 438 | |
---|
400 | 439 | {ADV748X_PAGE_IO, 0x0c, 0xe0}, /* Enable LLC_DLL & Double LLC Timing */ |
---|
401 | 440 | {ADV748X_PAGE_IO, 0x0e, 0xdd}, /* LLC/PIX/SPI PINS TRISTATED AUD */ |
---|
402 | | - /* Outputs Enabled */ |
---|
403 | | - {ADV748X_PAGE_IO, 0x10, 0xa0}, /* Enable 4-lane CSI Tx & Pixel Port */ |
---|
404 | | - |
---|
405 | | - {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ |
---|
406 | | - {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ |
---|
407 | | - {ADV748X_PAGE_TXA, 0xdb, 0x10}, /* ADI Required Write */ |
---|
408 | | - {ADV748X_PAGE_TXA, 0xd6, 0x07}, /* ADI Required Write */ |
---|
409 | | - {ADV748X_PAGE_TXA, 0xc4, 0x0a}, /* ADI Required Write */ |
---|
410 | | - {ADV748X_PAGE_TXA, 0x71, 0x33}, /* ADI Required Write */ |
---|
411 | | - {ADV748X_PAGE_TXA, 0x72, 0x11}, /* ADI Required Write */ |
---|
412 | | - {ADV748X_PAGE_TXA, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ |
---|
413 | | - |
---|
414 | | - {ADV748X_PAGE_TXA, 0x31, 0x82}, /* ADI Required Write */ |
---|
415 | | - {ADV748X_PAGE_TXA, 0x1e, 0x40}, /* ADI Required Write */ |
---|
416 | | - {ADV748X_PAGE_TXA, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ |
---|
417 | | - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ |
---|
418 | | - {ADV748X_PAGE_TXA, 0x00, 0x24 },/* Power-up CSI-TX */ |
---|
419 | | - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ |
---|
420 | | - {ADV748X_PAGE_TXA, 0xc1, 0x2b}, /* ADI Required Write */ |
---|
421 | | - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ |
---|
422 | | - {ADV748X_PAGE_TXA, 0x31, 0x80}, /* ADI Required Write */ |
---|
423 | 441 | |
---|
424 | 442 | {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ |
---|
425 | 443 | }; |
---|
426 | 444 | |
---|
427 | | -/* 02-01 Analog CVBS to MIPI TX-B CSI 1-Lane - */ |
---|
428 | | -/* Autodetect CVBS Single Ended In Ain 1 - MIPI Out */ |
---|
429 | | -static const struct adv748x_reg_value adv748x_init_txb_1lane[] = { |
---|
430 | | - |
---|
| 445 | +/* Initialize AFE core with YUV8 format. */ |
---|
| 446 | +static const struct adv748x_reg_value adv748x_init_afe[] = { |
---|
431 | 447 | {ADV748X_PAGE_IO, 0x00, 0x30}, /* Disable chip powerdown Rx */ |
---|
432 | 448 | {ADV748X_PAGE_IO, 0xf2, 0x01}, /* Enable I2C Read Auto-Increment */ |
---|
433 | 449 | |
---|
.. | .. |
---|
454 | 470 | {ADV748X_PAGE_SDP, 0x31, 0x12}, /* ADI Required Write */ |
---|
455 | 471 | {ADV748X_PAGE_SDP, 0xe6, 0x4f}, /* V bit end pos manually in NTSC */ |
---|
456 | 472 | |
---|
457 | | - /* Enable 1-Lane MIPI Tx, */ |
---|
458 | | - /* enable pixel output and route SD through Pixel port */ |
---|
459 | | - {ADV748X_PAGE_IO, 0x10, 0x70}, |
---|
460 | | - |
---|
461 | | - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ |
---|
462 | | - {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ |
---|
463 | | - {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */ |
---|
464 | | - {ADV748X_PAGE_TXB, 0xc4, 0x0a}, /* ADI Required Write */ |
---|
465 | | - {ADV748X_PAGE_TXB, 0x71, 0x33}, /* ADI Required Write */ |
---|
466 | | - {ADV748X_PAGE_TXB, 0x72, 0x11}, /* ADI Required Write */ |
---|
467 | | - {ADV748X_PAGE_TXB, 0xf0, 0x00}, /* i2c_dphy_pwdn - 1'b0 */ |
---|
468 | | - {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ |
---|
469 | | - {ADV748X_PAGE_TXB, 0x1e, 0x40}, /* ADI Required Write */ |
---|
470 | | - {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ |
---|
471 | | - |
---|
472 | | - {ADV748X_PAGE_WAIT, 0x00, 0x02},/* delay 2 */ |
---|
473 | | - {ADV748X_PAGE_TXB, 0x00, 0x21 },/* Power-up CSI-TX */ |
---|
474 | | - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ |
---|
475 | | - {ADV748X_PAGE_TXB, 0xc1, 0x2b}, /* ADI Required Write */ |
---|
476 | | - {ADV748X_PAGE_WAIT, 0x00, 0x01},/* delay 1 */ |
---|
477 | | - {ADV748X_PAGE_TXB, 0x31, 0x80}, /* ADI Required Write */ |
---|
478 | | - |
---|
479 | 473 | {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ |
---|
480 | 474 | }; |
---|
| 475 | + |
---|
| 476 | +static int adv748x_sw_reset(struct adv748x_state *state) |
---|
| 477 | +{ |
---|
| 478 | + int ret; |
---|
| 479 | + |
---|
| 480 | + ret = io_write(state, ADV748X_IO_REG_FF, ADV748X_IO_REG_FF_MAIN_RESET); |
---|
| 481 | + if (ret) |
---|
| 482 | + return ret; |
---|
| 483 | + |
---|
| 484 | + usleep_range(5000, 6000); |
---|
| 485 | + |
---|
| 486 | + /* Disable CEC Wakeup from power-down mode */ |
---|
| 487 | + ret = io_clrset(state, ADV748X_IO_REG_01, ADV748X_IO_REG_01_PWRDN_MASK, |
---|
| 488 | + ADV748X_IO_REG_01_PWRDNB); |
---|
| 489 | + if (ret) |
---|
| 490 | + return ret; |
---|
| 491 | + |
---|
| 492 | + /* Enable I2C Read Auto-Increment for consecutive reads */ |
---|
| 493 | + return io_write(state, ADV748X_IO_REG_F2, |
---|
| 494 | + ADV748X_IO_REG_F2_READ_AUTO_INC); |
---|
| 495 | +} |
---|
481 | 496 | |
---|
482 | 497 | static int adv748x_reset(struct adv748x_state *state) |
---|
483 | 498 | { |
---|
484 | 499 | int ret; |
---|
| 500 | + u8 regval = 0; |
---|
485 | 501 | |
---|
486 | | - ret = adv748x_write_regs(state, adv748x_sw_reset); |
---|
| 502 | + ret = adv748x_sw_reset(state); |
---|
487 | 503 | if (ret < 0) |
---|
488 | 504 | return ret; |
---|
489 | 505 | |
---|
.. | .. |
---|
491 | 507 | if (ret < 0) |
---|
492 | 508 | return ret; |
---|
493 | 509 | |
---|
494 | | - /* Init and power down TXA */ |
---|
495 | | - ret = adv748x_write_regs(state, adv748x_init_txa_4lane); |
---|
| 510 | + /* Initialize CP and AFE cores. */ |
---|
| 511 | + ret = adv748x_write_regs(state, adv748x_init_hdmi); |
---|
496 | 512 | if (ret) |
---|
497 | 513 | return ret; |
---|
498 | 514 | |
---|
499 | | - adv748x_txa_power(state, 0); |
---|
500 | | - |
---|
501 | | - /* Init and power down TXB */ |
---|
502 | | - ret = adv748x_write_regs(state, adv748x_init_txb_1lane); |
---|
| 515 | + ret = adv748x_write_regs(state, adv748x_init_afe); |
---|
503 | 516 | if (ret) |
---|
504 | 517 | return ret; |
---|
505 | 518 | |
---|
506 | | - adv748x_txb_power(state, 0); |
---|
| 519 | + /* Reset TXA and TXB */ |
---|
| 520 | + adv748x_tx_power(&state->txa, 1); |
---|
| 521 | + adv748x_tx_power(&state->txa, 0); |
---|
| 522 | + adv748x_tx_power(&state->txb, 1); |
---|
| 523 | + adv748x_tx_power(&state->txb, 0); |
---|
507 | 524 | |
---|
508 | 525 | /* Disable chip powerdown & Enable HDMI Rx block */ |
---|
509 | 526 | io_write(state, ADV748X_IO_PD, ADV748X_IO_PD_RX_EN); |
---|
510 | 527 | |
---|
511 | | - /* Enable 4-lane CSI Tx & Pixel Port */ |
---|
512 | | - io_write(state, ADV748X_IO_10, ADV748X_IO_10_CSI4_EN | |
---|
513 | | - ADV748X_IO_10_CSI1_EN | |
---|
514 | | - ADV748X_IO_10_PIX_OUT_EN); |
---|
| 528 | + /* Conditionally enable TXa and TXb. */ |
---|
| 529 | + if (is_tx_enabled(&state->txa)) |
---|
| 530 | + regval |= ADV748X_IO_10_CSI4_EN; |
---|
| 531 | + if (is_tx_enabled(&state->txb)) |
---|
| 532 | + regval |= ADV748X_IO_10_CSI1_EN; |
---|
| 533 | + io_write(state, ADV748X_IO_10, regval); |
---|
515 | 534 | |
---|
516 | 535 | /* Use vid_std and v_freq as freerun resolution for CP */ |
---|
517 | 536 | cp_clrset(state, ADV748X_CP_CLMP_POS, ADV748X_CP_CLMP_POS_DIS_AUTO, |
---|
.. | .. |
---|
562 | 581 | state->client->addr, ident); |
---|
563 | 582 | |
---|
564 | 583 | sd->entity.function = function; |
---|
565 | | - sd->entity.ops = &adv748x_media_ops; |
---|
| 584 | + sd->entity.ops = is_tx(adv748x_sd_to_csi2(sd)) ? |
---|
| 585 | + &adv748x_tx_media_ops : &adv748x_media_ops; |
---|
| 586 | +} |
---|
| 587 | + |
---|
| 588 | +static int adv748x_parse_csi2_lanes(struct adv748x_state *state, |
---|
| 589 | + unsigned int port, |
---|
| 590 | + struct device_node *ep) |
---|
| 591 | +{ |
---|
| 592 | + struct v4l2_fwnode_endpoint vep; |
---|
| 593 | + unsigned int num_lanes; |
---|
| 594 | + int ret; |
---|
| 595 | + |
---|
| 596 | + if (port != ADV748X_PORT_TXA && port != ADV748X_PORT_TXB) |
---|
| 597 | + return 0; |
---|
| 598 | + |
---|
| 599 | + vep.bus_type = V4L2_MBUS_CSI2_DPHY; |
---|
| 600 | + ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &vep); |
---|
| 601 | + if (ret) |
---|
| 602 | + return ret; |
---|
| 603 | + |
---|
| 604 | + num_lanes = vep.bus.mipi_csi2.num_data_lanes; |
---|
| 605 | + |
---|
| 606 | + if (vep.base.port == ADV748X_PORT_TXA) { |
---|
| 607 | + if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4) { |
---|
| 608 | + adv_err(state, "TXA: Invalid number (%u) of lanes\n", |
---|
| 609 | + num_lanes); |
---|
| 610 | + return -EINVAL; |
---|
| 611 | + } |
---|
| 612 | + |
---|
| 613 | + state->txa.num_lanes = num_lanes; |
---|
| 614 | + state->txa.active_lanes = num_lanes; |
---|
| 615 | + adv_dbg(state, "TXA: using %u lanes\n", state->txa.num_lanes); |
---|
| 616 | + } |
---|
| 617 | + |
---|
| 618 | + if (vep.base.port == ADV748X_PORT_TXB) { |
---|
| 619 | + if (num_lanes != 1) { |
---|
| 620 | + adv_err(state, "TXB: Invalid number (%u) of lanes\n", |
---|
| 621 | + num_lanes); |
---|
| 622 | + return -EINVAL; |
---|
| 623 | + } |
---|
| 624 | + |
---|
| 625 | + state->txb.num_lanes = num_lanes; |
---|
| 626 | + state->txb.active_lanes = num_lanes; |
---|
| 627 | + adv_dbg(state, "TXB: using %u lanes\n", state->txb.num_lanes); |
---|
| 628 | + } |
---|
| 629 | + |
---|
| 630 | + return 0; |
---|
566 | 631 | } |
---|
567 | 632 | |
---|
568 | 633 | static int adv748x_parse_dt(struct adv748x_state *state) |
---|
.. | .. |
---|
571 | 636 | struct of_endpoint ep; |
---|
572 | 637 | bool out_found = false; |
---|
573 | 638 | bool in_found = false; |
---|
| 639 | + int ret; |
---|
574 | 640 | |
---|
575 | 641 | for_each_endpoint_of_node(state->dev->of_node, ep_np) { |
---|
576 | 642 | of_graph_parse_endpoint(ep_np, &ep); |
---|
.. | .. |
---|
601 | 667 | in_found = true; |
---|
602 | 668 | else |
---|
603 | 669 | out_found = true; |
---|
| 670 | + |
---|
| 671 | + /* Store number of CSI-2 lanes used for TXA and TXB. */ |
---|
| 672 | + ret = adv748x_parse_csi2_lanes(state, ep.port, ep_np); |
---|
| 673 | + if (ret) |
---|
| 674 | + return ret; |
---|
604 | 675 | } |
---|
605 | 676 | |
---|
606 | 677 | return in_found && out_found ? 0 : -ENODEV; |
---|
.. | .. |
---|
614 | 685 | of_node_put(state->endpoints[i]); |
---|
615 | 686 | } |
---|
616 | 687 | |
---|
617 | | -static int adv748x_probe(struct i2c_client *client, |
---|
618 | | - const struct i2c_device_id *id) |
---|
| 688 | +static int adv748x_probe(struct i2c_client *client) |
---|
619 | 689 | { |
---|
620 | 690 | struct adv748x_state *state; |
---|
621 | 691 | int ret; |
---|
.. | .. |
---|
624 | 694 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
---|
625 | 695 | return -EIO; |
---|
626 | 696 | |
---|
627 | | - state = kzalloc(sizeof(struct adv748x_state), GFP_KERNEL); |
---|
| 697 | + state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); |
---|
628 | 698 | if (!state) |
---|
629 | 699 | return -ENOMEM; |
---|
630 | 700 | |
---|
.. | .. |
---|
722 | 792 | adv748x_dt_cleanup(state); |
---|
723 | 793 | err_free_mutex: |
---|
724 | 794 | mutex_destroy(&state->mutex); |
---|
725 | | - kfree(state); |
---|
726 | 795 | |
---|
727 | 796 | return ret; |
---|
728 | 797 | } |
---|
.. | .. |
---|
741 | 810 | adv748x_dt_cleanup(state); |
---|
742 | 811 | mutex_destroy(&state->mutex); |
---|
743 | 812 | |
---|
744 | | - kfree(state); |
---|
745 | | - |
---|
746 | 813 | return 0; |
---|
747 | 814 | } |
---|
748 | | - |
---|
749 | | -static const struct i2c_device_id adv748x_id[] = { |
---|
750 | | - { "adv7481", 0 }, |
---|
751 | | - { "adv7482", 0 }, |
---|
752 | | - { }, |
---|
753 | | -}; |
---|
754 | | -MODULE_DEVICE_TABLE(i2c, adv748x_id); |
---|
755 | 815 | |
---|
756 | 816 | static const struct of_device_id adv748x_of_table[] = { |
---|
757 | 817 | { .compatible = "adi,adv7481", }, |
---|
.. | .. |
---|
765 | 825 | .name = "adv748x", |
---|
766 | 826 | .of_match_table = adv748x_of_table, |
---|
767 | 827 | }, |
---|
768 | | - .probe = adv748x_probe, |
---|
| 828 | + .probe_new = adv748x_probe, |
---|
769 | 829 | .remove = adv748x_remove, |
---|
770 | | - .id_table = adv748x_id, |
---|
771 | 830 | }; |
---|
772 | 831 | |
---|
773 | 832 | module_i2c_driver(adv748x_driver); |
---|
774 | 833 | |
---|
775 | 834 | MODULE_AUTHOR("Kieran Bingham <kieran.bingham@ideasonboard.com>"); |
---|
776 | 835 | MODULE_DESCRIPTION("ADV748X video decoder"); |
---|
777 | | -MODULE_LICENSE("GPL v2"); |
---|
| 836 | +MODULE_LICENSE("GPL"); |
---|