diff options
author | Sylwester Nawrocki <s.nawrocki@samsung.com> | 2012-09-05 09:10:37 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-10-01 19:27:23 -0400 |
commit | 99c6902cdd66153ef6f0a272d0442d31bb5f6eb1 (patch) | |
tree | 67047f9b33be65dc1054ba0d7e6417d35c0fd717 /drivers/media/platform | |
parent | bae061b46e522163e2e499af05788c282492836e (diff) |
[media] s5p-csis: Add transmission errors logging
Add hardware event/error counters which can be dumped into the kernel
log through VIDIOC_LOG_STATUS ioctl. The counters are reset in each
s_stream(1) call. Any errors are logged after streaming is turned off.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/platform')
-rw-r--r-- | drivers/media/platform/s5p-fimc/mipi-csis.c | 160 |
1 files changed, 140 insertions, 20 deletions
diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index 2f73d9e3d0b7..8e4eed8414bd 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c | |||
@@ -31,7 +31,7 @@ | |||
31 | 31 | ||
32 | static int debug; | 32 | static int debug; |
33 | module_param(debug, int, 0644); | 33 | module_param(debug, int, 0644); |
34 | MODULE_PARM_DESC(debug, "Debug level (0-1)"); | 34 | MODULE_PARM_DESC(debug, "Debug level (0-2)"); |
35 | 35 | ||
36 | /* Register map definition */ | 36 | /* Register map definition */ |
37 | 37 | ||
@@ -60,10 +60,38 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); | |||
60 | #define S5PCSIS_CFG_FMT_MASK (0x3f << 2) | 60 | #define S5PCSIS_CFG_FMT_MASK (0x3f << 2) |
61 | #define S5PCSIS_CFG_NR_LANE_MASK 3 | 61 | #define S5PCSIS_CFG_NR_LANE_MASK 3 |
62 | 62 | ||
63 | /* Interrupt mask. */ | 63 | /* Interrupt mask */ |
64 | #define S5PCSIS_INTMSK 0x10 | 64 | #define S5PCSIS_INTMSK 0x10 |
65 | #define S5PCSIS_INTMSK_EN_ALL 0xf000003f | 65 | #define S5PCSIS_INTMSK_EN_ALL 0xf000103f |
66 | #define S5PCSIS_INTMSK_EVEN_BEFORE (1 << 31) | ||
67 | #define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30) | ||
68 | #define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29) | ||
69 | #define S5PCSIS_INTMSK_ODD_AFTER (1 << 28) | ||
70 | #define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 12) | ||
71 | #define S5PCSIS_INTMSK_ERR_LOST_FS (1 << 5) | ||
72 | #define S5PCSIS_INTMSK_ERR_LOST_FE (1 << 4) | ||
73 | #define S5PCSIS_INTMSK_ERR_OVER (1 << 3) | ||
74 | #define S5PCSIS_INTMSK_ERR_ECC (1 << 2) | ||
75 | #define S5PCSIS_INTMSK_ERR_CRC (1 << 1) | ||
76 | #define S5PCSIS_INTMSK_ERR_UNKNOWN (1 << 0) | ||
77 | |||
78 | /* Interrupt source */ | ||
66 | #define S5PCSIS_INTSRC 0x14 | 79 | #define S5PCSIS_INTSRC 0x14 |
80 | #define S5PCSIS_INTSRC_EVEN_BEFORE (1 << 31) | ||
81 | #define S5PCSIS_INTSRC_EVEN_AFTER (1 << 30) | ||
82 | #define S5PCSIS_INTSRC_EVEN (0x3 << 30) | ||
83 | #define S5PCSIS_INTSRC_ODD_BEFORE (1 << 29) | ||
84 | #define S5PCSIS_INTSRC_ODD_AFTER (1 << 28) | ||
85 | #define S5PCSIS_INTSRC_ODD (0x3 << 28) | ||
86 | #define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28) | ||
87 | #define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12) | ||
88 | #define S5PCSIS_INTSRC_ERR_LOST_FS (1 << 5) | ||
89 | #define S5PCSIS_INTSRC_ERR_LOST_FE (1 << 4) | ||
90 | #define S5PCSIS_INTSRC_ERR_OVER (1 << 3) | ||
91 | #define S5PCSIS_INTSRC_ERR_ECC (1 << 2) | ||
92 | #define S5PCSIS_INTSRC_ERR_CRC (1 << 1) | ||
93 | #define S5PCSIS_INTSRC_ERR_UNKNOWN (1 << 0) | ||
94 | #define S5PCSIS_INTSRC_ERRORS 0xf03f | ||
67 | 95 | ||
68 | /* Pixel resolution */ | 96 | /* Pixel resolution */ |
69 | #define S5PCSIS_RESOL 0x2c | 97 | #define S5PCSIS_RESOL 0x2c |
@@ -93,6 +121,29 @@ enum { | |||
93 | ST_SUSPENDED = 4, | 121 | ST_SUSPENDED = 4, |
94 | }; | 122 | }; |
95 | 123 | ||
124 | struct s5pcsis_event { | ||
125 | u32 mask; | ||
126 | const char * const name; | ||
127 | unsigned int counter; | ||
128 | }; | ||
129 | |||
130 | static const struct s5pcsis_event s5pcsis_events[] = { | ||
131 | /* Errors */ | ||
132 | { S5PCSIS_INTSRC_ERR_SOT_HS, "SOT Error" }, | ||
133 | { S5PCSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" }, | ||
134 | { S5PCSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" }, | ||
135 | { S5PCSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" }, | ||
136 | { S5PCSIS_INTSRC_ERR_ECC, "ECC Error" }, | ||
137 | { S5PCSIS_INTSRC_ERR_CRC, "CRC Error" }, | ||
138 | { S5PCSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" }, | ||
139 | /* Non-image data receive events */ | ||
140 | { S5PCSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" }, | ||
141 | { S5PCSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" }, | ||
142 | { S5PCSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" }, | ||
143 | { S5PCSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" }, | ||
144 | }; | ||
145 | #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events) | ||
146 | |||
96 | /** | 147 | /** |
97 | * struct csis_state - the driver's internal state data structure | 148 | * struct csis_state - the driver's internal state data structure |
98 | * @lock: mutex serializing the subdev and power management operations, | 149 | * @lock: mutex serializing the subdev and power management operations, |
@@ -101,11 +152,14 @@ enum { | |||
101 | * @sd: v4l2_subdev associated with CSIS device instance | 152 | * @sd: v4l2_subdev associated with CSIS device instance |
102 | * @pdev: CSIS platform device | 153 | * @pdev: CSIS platform device |
103 | * @regs: mmaped I/O registers memory | 154 | * @regs: mmaped I/O registers memory |
155 | * @supplies: CSIS regulator supplies | ||
104 | * @clock: CSIS clocks | 156 | * @clock: CSIS clocks |
105 | * @irq: requested s5p-mipi-csis irq number | 157 | * @irq: requested s5p-mipi-csis irq number |
106 | * @flags: the state variable for power and streaming control | 158 | * @flags: the state variable for power and streaming control |
107 | * @csis_fmt: current CSIS pixel format | 159 | * @csis_fmt: current CSIS pixel format |
108 | * @format: common media bus format for the source and sink pad | 160 | * @format: common media bus format for the source and sink pad |
161 | * @slock: spinlock protecting structure members below | ||
162 | * @events: MIPI-CSIS event (error) counters | ||
109 | */ | 163 | */ |
110 | struct csis_state { | 164 | struct csis_state { |
111 | struct mutex lock; | 165 | struct mutex lock; |
@@ -119,6 +173,9 @@ struct csis_state { | |||
119 | u32 flags; | 173 | u32 flags; |
120 | const struct csis_pix_format *csis_fmt; | 174 | const struct csis_pix_format *csis_fmt; |
121 | struct v4l2_mbus_framefmt format; | 175 | struct v4l2_mbus_framefmt format; |
176 | |||
177 | struct spinlock slock; | ||
178 | struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; | ||
122 | }; | 179 | }; |
123 | 180 | ||
124 | /** | 181 | /** |
@@ -292,17 +349,6 @@ err: | |||
292 | return -ENXIO; | 349 | return -ENXIO; |
293 | } | 350 | } |
294 | 351 | ||
295 | static int s5pcsis_s_power(struct v4l2_subdev *sd, int on) | ||
296 | { | ||
297 | struct csis_state *state = sd_to_csis_state(sd); | ||
298 | struct device *dev = &state->pdev->dev; | ||
299 | |||
300 | if (on) | ||
301 | return pm_runtime_get_sync(dev); | ||
302 | |||
303 | return pm_runtime_put_sync(dev); | ||
304 | } | ||
305 | |||
306 | static void s5pcsis_start_stream(struct csis_state *state) | 352 | static void s5pcsis_start_stream(struct csis_state *state) |
307 | { | 353 | { |
308 | s5pcsis_reset(state); | 354 | s5pcsis_reset(state); |
@@ -317,7 +363,47 @@ static void s5pcsis_stop_stream(struct csis_state *state) | |||
317 | s5pcsis_system_enable(state, false); | 363 | s5pcsis_system_enable(state, false); |
318 | } | 364 | } |
319 | 365 | ||
320 | /* v4l2_subdev operations */ | 366 | static void s5pcsis_clear_counters(struct csis_state *state) |
367 | { | ||
368 | unsigned long flags; | ||
369 | int i; | ||
370 | |||
371 | spin_lock_irqsave(&state->slock, flags); | ||
372 | for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) | ||
373 | state->events[i].counter = 0; | ||
374 | spin_unlock_irqrestore(&state->slock, flags); | ||
375 | } | ||
376 | |||
377 | static void s5pcsis_log_counters(struct csis_state *state, bool non_errors) | ||
378 | { | ||
379 | int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4; | ||
380 | unsigned long flags; | ||
381 | |||
382 | spin_lock_irqsave(&state->slock, flags); | ||
383 | |||
384 | for (i--; i >= 0; i--) | ||
385 | if (state->events[i].counter >= 0) | ||
386 | v4l2_info(&state->sd, "%s events: %d\n", | ||
387 | state->events[i].name, | ||
388 | state->events[i].counter); | ||
389 | |||
390 | spin_unlock_irqrestore(&state->slock, flags); | ||
391 | } | ||
392 | |||
393 | /* | ||
394 | * V4L2 subdev operations | ||
395 | */ | ||
396 | static int s5pcsis_s_power(struct v4l2_subdev *sd, int on) | ||
397 | { | ||
398 | struct csis_state *state = sd_to_csis_state(sd); | ||
399 | struct device *dev = &state->pdev->dev; | ||
400 | |||
401 | if (on) | ||
402 | return pm_runtime_get_sync(dev); | ||
403 | |||
404 | return pm_runtime_put_sync(dev); | ||
405 | } | ||
406 | |||
321 | static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) | 407 | static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) |
322 | { | 408 | { |
323 | struct csis_state *state = sd_to_csis_state(sd); | 409 | struct csis_state *state = sd_to_csis_state(sd); |
@@ -327,10 +413,12 @@ static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) | |||
327 | __func__, enable, state->flags); | 413 | __func__, enable, state->flags); |
328 | 414 | ||
329 | if (enable) { | 415 | if (enable) { |
416 | s5pcsis_clear_counters(state); | ||
330 | ret = pm_runtime_get_sync(&state->pdev->dev); | 417 | ret = pm_runtime_get_sync(&state->pdev->dev); |
331 | if (ret && ret != 1) | 418 | if (ret && ret != 1) |
332 | return ret; | 419 | return ret; |
333 | } | 420 | } |
421 | |||
334 | mutex_lock(&state->lock); | 422 | mutex_lock(&state->lock); |
335 | if (enable) { | 423 | if (enable) { |
336 | if (state->flags & ST_SUSPENDED) { | 424 | if (state->flags & ST_SUSPENDED) { |
@@ -342,6 +430,8 @@ static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) | |||
342 | } else { | 430 | } else { |
343 | s5pcsis_stop_stream(state); | 431 | s5pcsis_stop_stream(state); |
344 | state->flags &= ~ST_STREAMING; | 432 | state->flags &= ~ST_STREAMING; |
433 | if (debug > 0) | ||
434 | s5pcsis_log_counters(state, true); | ||
345 | } | 435 | } |
346 | unlock: | 436 | unlock: |
347 | mutex_unlock(&state->lock); | 437 | mutex_unlock(&state->lock); |
@@ -439,6 +529,14 @@ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | |||
439 | return 0; | 529 | return 0; |
440 | } | 530 | } |
441 | 531 | ||
532 | static int s5pcsis_log_status(struct v4l2_subdev *sd) | ||
533 | { | ||
534 | struct csis_state *state = sd_to_csis_state(sd); | ||
535 | |||
536 | s5pcsis_log_counters(state, true); | ||
537 | return 0; | ||
538 | } | ||
539 | |||
442 | static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | 540 | static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) |
443 | { | 541 | { |
444 | struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); | 542 | struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); |
@@ -458,6 +556,7 @@ static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = { | |||
458 | 556 | ||
459 | static struct v4l2_subdev_core_ops s5pcsis_core_ops = { | 557 | static struct v4l2_subdev_core_ops s5pcsis_core_ops = { |
460 | .s_power = s5pcsis_s_power, | 558 | .s_power = s5pcsis_s_power, |
559 | .log_status = s5pcsis_log_status, | ||
461 | }; | 560 | }; |
462 | 561 | ||
463 | static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = { | 562 | static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = { |
@@ -479,12 +578,29 @@ static struct v4l2_subdev_ops s5pcsis_subdev_ops = { | |||
479 | static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) | 578 | static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) |
480 | { | 579 | { |
481 | struct csis_state *state = dev_id; | 580 | struct csis_state *state = dev_id; |
482 | u32 val; | 581 | unsigned long flags; |
483 | 582 | u32 status; | |
484 | /* Just clear the interrupt pending bits. */ | 583 | |
485 | val = s5pcsis_read(state, S5PCSIS_INTSRC); | 584 | status = s5pcsis_read(state, S5PCSIS_INTSRC); |
486 | s5pcsis_write(state, S5PCSIS_INTSRC, val); | 585 | |
586 | spin_lock_irqsave(&state->slock, flags); | ||
587 | |||
588 | /* Update the event/error counters */ | ||
589 | if ((status & S5PCSIS_INTSRC_ERRORS) || debug) { | ||
590 | int i; | ||
591 | for (i = 0; i < S5PCSIS_NUM_EVENTS; i++) { | ||
592 | if (!(status & state->events[i].mask)) | ||
593 | continue; | ||
594 | state->events[i].counter++; | ||
595 | v4l2_dbg(2, debug, &state->sd, "%s: %d\n", | ||
596 | state->events[i].name, | ||
597 | state->events[i].counter); | ||
598 | } | ||
599 | v4l2_dbg(2, debug, &state->sd, "status: %08x\n", status); | ||
600 | } | ||
601 | spin_unlock_irqrestore(&state->slock, flags); | ||
487 | 602 | ||
603 | s5pcsis_write(state, S5PCSIS_INTSRC, status); | ||
488 | return IRQ_HANDLED; | 604 | return IRQ_HANDLED; |
489 | } | 605 | } |
490 | 606 | ||
@@ -501,6 +617,8 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) | |||
501 | return -ENOMEM; | 617 | return -ENOMEM; |
502 | 618 | ||
503 | mutex_init(&state->lock); | 619 | mutex_init(&state->lock); |
620 | spin_lock_init(&state->slock); | ||
621 | |||
504 | state->pdev = pdev; | 622 | state->pdev = pdev; |
505 | 623 | ||
506 | pdata = pdev->dev.platform_data; | 624 | pdata = pdev->dev.platform_data; |
@@ -577,6 +695,8 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) | |||
577 | /* .. and a pointer to the subdev. */ | 695 | /* .. and a pointer to the subdev. */ |
578 | platform_set_drvdata(pdev, &state->sd); | 696 | platform_set_drvdata(pdev, &state->sd); |
579 | 697 | ||
698 | memcpy(state->events, s5pcsis_events, sizeof(state->events)); | ||
699 | |||
580 | pm_runtime_enable(&pdev->dev); | 700 | pm_runtime_enable(&pdev->dev); |
581 | return 0; | 701 | return 0; |
582 | 702 | ||