aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/platform
diff options
context:
space:
mode:
authorSylwester Nawrocki <s.nawrocki@samsung.com>2012-09-05 09:10:37 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-10-01 19:27:23 -0400
commit99c6902cdd66153ef6f0a272d0442d31bb5f6eb1 (patch)
tree67047f9b33be65dc1054ba0d7e6417d35c0fd717 /drivers/media/platform
parentbae061b46e522163e2e499af05788c282492836e (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.c160
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
32static int debug; 32static int debug;
33module_param(debug, int, 0644); 33module_param(debug, int, 0644);
34MODULE_PARM_DESC(debug, "Debug level (0-1)"); 34MODULE_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
124struct s5pcsis_event {
125 u32 mask;
126 const char * const name;
127 unsigned int counter;
128};
129
130static 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 */
110struct csis_state { 164struct 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
295static 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
306static void s5pcsis_start_stream(struct csis_state *state) 352static 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 */ 366static 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
377static 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 */
396static 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
321static int s5pcsis_s_stream(struct v4l2_subdev *sd, int enable) 407static 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 }
346unlock: 436unlock:
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
532static 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
442static int s5pcsis_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 540static 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
459static struct v4l2_subdev_core_ops s5pcsis_core_ops = { 557static 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
463static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = { 562static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
@@ -479,12 +578,29 @@ static struct v4l2_subdev_ops s5pcsis_subdev_ops = {
479static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) 578static 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