linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch] [media] tw686x: off by one bugs in tw686x_fields_map()
@ 2016-04-27  8:09 Dan Carpenter
  2016-04-27 10:31 ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 12+ messages in thread
From: Dan Carpenter @ 2016-04-27  8:09 UTC (permalink / raw)
  To: Ezequiel Garcia, Mauro Carvalho Chehab; +Cc: linux-media, kernel-janitors

The > ARRAY_SIZE() should be >= ARRAY_SIZE().  Also this is a slightly
unrelated cleanup but I replaced the magic numbers 30 and 25 with
ARRAY_SIZE() - 1.

Fixes: 363d79f1d5bd ('[media] tw686x: Don't go past array')
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>

diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index d2a0147..7b87f27 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -64,12 +64,12 @@ static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
 	unsigned int i;
 
 	if (std & V4L2_STD_525_60) {
-		if (fps > ARRAY_SIZE(std_525_60))
-			fps = 30;
+		if (fps >= ARRAY_SIZE(std_525_60))
+			fps = ARRAY_SIZE(std_525_60) - 1;
 		i = std_525_60[fps];
 	} else {
-		if (fps > ARRAY_SIZE(std_625_50))
-			fps = 25;
+		if (fps >= ARRAY_SIZE(std_625_50))
+			fps = ARRAY_SIZE(std_625_50) - 1;
 		i = std_625_50[fps];
 	}
 

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [patch] [media] tw686x: off by one bugs in tw686x_fields_map()
  2016-04-27  8:09 [patch] [media] tw686x: off by one bugs in tw686x_fields_map() Dan Carpenter
@ 2016-04-27 10:31 ` Mauro Carvalho Chehab
  2016-04-27 10:36   ` Dan Carpenter
  2016-04-27 10:40   ` Mauro Carvalho Chehab
  0 siblings, 2 replies; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-04-27 10:31 UTC (permalink / raw)
  To: Dan Carpenter; +Cc: Ezequiel Garcia, linux-media, kernel-janitors

Hi Dan,

Em Wed, 27 Apr 2016 11:09:28 +0300
Dan Carpenter <dan.carpenter@oracle.com> escreveu:

> The > ARRAY_SIZE() should be >= ARRAY_SIZE(). 

I actually did this fix when I produced the patch, just I forgot to fold
it when merging. Anyway, this was fixed upstream by this patch:
	https://git.linuxtv.org/media_tree.git/commit/?id=45c175c4ae9695d6d2f30a45ab7f3866cfac184b

> Also this is a slightly
> unrelated cleanup but I replaced the magic numbers 30 and 25 with
> ARRAY_SIZE() - 1.

I don't like magic numbers, but, in this very specific case, setting
frames per second (fps) var to 25 or 30 makes much more sense. The
rationale is that:

The V4L2_STD_525_60 macro is for the Countries where the power line 
uses 60Hz, and V4L2_STD_625_50 for the Countries where the power line
is 50Hz.

The broadcast TV sends frames in half of this frequency, so, for
V4L2_STD_525_60, fps = 30, while, for V4L2_STD_625_50, fps = 25.

So, in this very specific case, IMHO, it is better to see 25 or 30 there,
instead of ARRAY_SIZE().

That's said, I guess one improvement would be to get rid of those two
arrays and replacing them by a formula, like:

               	i = (max_fps / 2 + 15 * fps) / max_fps;
                if (i > 14)
                        i = 0;

I'll propose such patch for evaluation.

Regards,
Mauro

> 
> Fixes: 363d79f1d5bd ('[media] tw686x: Don't go past array')
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> 
> diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
> index d2a0147..7b87f27 100644
> --- a/drivers/media/pci/tw686x/tw686x-video.c
> +++ b/drivers/media/pci/tw686x/tw686x-video.c
> @@ -64,12 +64,12 @@ static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
>  	unsigned int i;
>  
>  	if (std & V4L2_STD_525_60) {
> -		if (fps > ARRAY_SIZE(std_525_60))
> -			fps = 30;
> +		if (fps >= ARRAY_SIZE(std_525_60))
> +			fps = ARRAY_SIZE(std_525_60) - 1;
>  		i = std_525_60[fps];
>  	} else {
> -		if (fps > ARRAY_SIZE(std_625_50))
> -			fps = 25;
> +		if (fps >= ARRAY_SIZE(std_625_50))
> +			fps = ARRAY_SIZE(std_625_50) - 1;
>  		i = std_625_50[fps];
>  	}
>  


-- 
Thanks,
Mauro

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [patch] [media] tw686x: off by one bugs in tw686x_fields_map()
  2016-04-27 10:31 ` Mauro Carvalho Chehab
@ 2016-04-27 10:36   ` Dan Carpenter
  2016-04-27 10:40   ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 12+ messages in thread
From: Dan Carpenter @ 2016-04-27 10:36 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Ezequiel Garcia, linux-media, kernel-janitors

On Wed, Apr 27, 2016 at 07:31:59AM -0300, Mauro Carvalho Chehab wrote:
> I don't like magic numbers, but, in this very specific case, setting
> frames per second (fps) var to 25 or 30 makes much more sense.

Fine fine.  I buy that rationale.

regards,
dan carpenter


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [patch] [media] tw686x: off by one bugs in tw686x_fields_map()
  2016-04-27 10:31 ` Mauro Carvalho Chehab
  2016-04-27 10:36   ` Dan Carpenter
@ 2016-04-27 10:40   ` Mauro Carvalho Chehab
  2016-04-27 11:01     ` [PATCH] tw686x: use a formula instead of two tables for div Mauro Carvalho Chehab
  1 sibling, 1 reply; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-04-27 10:40 UTC (permalink / raw)
  To: Dan Carpenter; +Cc: Ezequiel Garcia, linux-media, kernel-janitors

Em Wed, 27 Apr 2016 07:31:59 -0300
Mauro Carvalho Chehab <mchehab@osg.samsung.com> escreveu:

> Hi Dan,
> 
> Em Wed, 27 Apr 2016 11:09:28 +0300
> Dan Carpenter <dan.carpenter@oracle.com> escreveu:
> 
> > The > ARRAY_SIZE() should be >= ARRAY_SIZE(). 
> 
> I actually did this fix when I produced the patch, just I forgot to fold
> it when merging. Anyway, this was fixed upstream by this patch:
> 	https://git.linuxtv.org/media_tree.git/commit/?id=45c175c4ae9695d6d2f30a45ab7f3866cfac184b
> 
> > Also this is a slightly
> > unrelated cleanup but I replaced the magic numbers 30 and 25 with
> > ARRAY_SIZE() - 1.
> 
> I don't like magic numbers, but, in this very specific case, setting
> frames per second (fps) var to 25 or 30 makes much more sense. The
> rationale is that:
> 
> The V4L2_STD_525_60 macro is for the Countries where the power line 
> uses 60Hz, and V4L2_STD_625_50 for the Countries where the power line
> is 50Hz.
> 
> The broadcast TV sends frames in half of this frequency, so, for
> V4L2_STD_525_60, fps = 30, while, for V4L2_STD_625_50, fps = 25.
> 
> So, in this very specific case, IMHO, it is better to see 25 or 30 there,
> instead of ARRAY_SIZE().
> 
> That's said, I guess one improvement would be to get rid of those two
> arrays and replacing them by a formula, like:
> 
>                	i = (max_fps / 2 + 15 * fps) / max_fps;
>                 if (i > 14)
>                         i = 0;
> 
> I'll propose such patch for evaluation.

I did some tests to check how that array was determined, using 
the enclosed program.

It seems that the table was generated using this formula:

               	i2 = (adjust + 15 * fps) / max_fps;
                if (fps && !i2)
                       	i2 = 1;
                if (i2 > 14)
                        i2 = 0;

Where adjust is given by:
	adjust = 12; /* actually, any value between 11 and 14 */

Using it, I got these results:


60 Hz
	    fps 0, i1=0, i2=0
	    fps 1, i1=1, i2=1
	    fps 2, i1=1, i2=1
	    fps 3, i1=1, i2=1
	    fps 4, i1=2, i2=2
	    fps 5, i1=2, i2=2
	    fps 6, i1=3, i2=3
	    fps 7, i1=3, i2=3
	    fps 8, i1=4, i2=4
	    fps 9, i1=4, i2=4
	    fps 10, i1=5, i2=5
	    fps 11, i1=5, i2=5
	    fps 12, i1=6, i2=6
	    fps 13, i1=6, i2=6
	    fps 14, i1=7, i2=7
	    fps 15, i1=7, i2=7
	    fps 16, i1=8, i2=8
	    fps 17, i1=8, i2=8
	    fps 18, i1=9, i2=9
	    fps 19, i1=9, i2=9
	    fps 20, i1=10, i2=10
	    fps 21, i1=10, i2=10
	    fps 22, i1=11, i2=11
	    fps 23, i1=11, i2=11
	    fps 24, i1=12, i2=12
	    fps 25, i1=12, i2=12
	    fps 26, i1=13, i2=13
	    fps 27, i1=13, i2=13
	    fps 28, i1=14, i2=14
	NOK fps 29, i1=0, i2=14
	    fps 30, i1=0, i2=0

50 Hz
	    fps 0, i1=0, i2=0
	    fps 1, i1=1, i2=1
	    fps 2, i1=1, i2=1
	    fps 3, i1=2, i2=2
	NOK fps 4, i1=3, i2=2
	    fps 5, i1=3, i2=3
	    fps 6, i1=4, i2=4
	    fps 7, i1=4, i2=4
	    fps 8, i1=5, i2=5
	    fps 9, i1=5, i2=5
	    fps 10, i1=6, i2=6
	    fps 11, i1=7, i2=7
	    fps 12, i1=7, i2=7
	    fps 13, i1=8, i2=8
	    fps 14, i1=8, i2=8
	    fps 15, i1=9, i2=9
	    fps 16, i1=10, i2=10
	    fps 17, i1=10, i2=10
	    fps 18, i1=11, i2=11
	    fps 19, i1=11, i2=11
	    fps 20, i1=12, i2=12
	    fps 21, i1=13, i2=13
	    fps 22, i1=13, i2=13
	    fps 23, i1=14, i2=14
	    fps 24, i1=14, i2=14
	    fps 25, i1=0, i2=0

IMHO, the two values that are different at the table are wrong ;)

I would also round to the closest number, with probably makes more
sense, and fits well at the API requirements.

The small program to test itis enclosed below. I'll send a patch
getting rid of those tables.

Regards,
Mauro

===

#include <stdio.h>

static const unsigned int std_625_50[26] = {
                0, 1, 1, 2,  3,  3,  4,  4,  5,  5,  6,  7,  7,
                   8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 0
};

static const unsigned int std_525_60[31] = {
                0, 1, 1, 1, 2,  2,  3,  3,  4,  4,  5,  5,  6,  6, 7, 7,
                   8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0
};

void calc_fps(unsigned int max_fps)
{
	unsigned int i1, i2, fps, adjust;

//	adjust = max_fps / 2;
	adjust = 12; /* 11 a 14 */
	for (fps = 0; fps <= max_fps; fps++) {
		if (max_fps == 30)
			i1 = std_525_60[fps];
		else
			i1 = std_625_50[fps];

		i2 = (adjust + 15 * fps) / max_fps;
		if (fps && !i2)
			i2 = 1;
		if (i2 > 14)
			i2 = 0;

//if (i1 != i2)
		printf("\t%s fps %d, i1=%d, i2=%d\n",
			(i1 == i2)? "   " : "NOK",
			fps, i1, i2);
	}
}

void main(void)
{
	printf ("60 Hz\n");
	calc_fps(30);

	printf ("\n50 Hz\n");
	calc_fps(25);
}



-- 
Thanks,
Mauro

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] tw686x: use a formula instead of two tables for div
  2016-04-27 10:40   ` Mauro Carvalho Chehab
@ 2016-04-27 11:01     ` Mauro Carvalho Chehab
  2016-04-27 12:00       ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-04-27 11:01 UTC (permalink / raw)
  To: Linux Media Mailing List
  Cc: Mauro Carvalho Chehab, Mauro Carvalho Chehab, Ezequiel Garcia

Instead of using two tables to estimate the temporal decimation
factor, use a formula. This allows to get the closest fps, with
sounds better than the current tables.

Compile-tested only.

Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
---
 drivers/media/pci/tw686x/tw686x-video.c | 34 ++++++++++++---------------------
 1 file changed, 12 insertions(+), 22 deletions(-)

diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index 253e10823ba3..0210fa304e4c 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -50,28 +50,18 @@ static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
 		0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
 		0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
 	};
-
-	static const unsigned int std_625_50[26] = {
-		0, 1, 1, 2,  3,  3,  4,  4,  5,  5,  6,  7,  7,
-		   8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 0
-	};
-
-	static const unsigned int std_525_60[31] = {
-		0, 1, 1, 1, 2,  2,  3,  3,  4,  4,  5,  5,  6,  6, 7, 7,
-		   8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0
-	};
-
-	unsigned int i;
-
-	if (std & V4L2_STD_525_60) {
-		if (fps >= ARRAY_SIZE(std_525_60))
-			fps = 30;
-		i = std_525_60[fps];
-	} else {
-		if (fps >= ARRAY_SIZE(std_625_50))
-			fps = 25;
-		i = std_625_50[fps];
-	}
+	unsigned int i, max_fps;
+
+	if (std & V4L2_STD_525_60)
+		max_fps = 30;
+	else
+		max_fps = 25;
+
+	i = DIV_ROUND_CLOSEST(15 * fps, max_fps);
+	if (!i)
+		i = 1;	/* Min possible fps */
+	else if (i > 14)
+		i = 0;	/* fps = max_fps */
 
 	return map[i];
 }
-- 
2.5.5


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] tw686x: use a formula instead of two tables for div
  2016-04-27 11:01     ` [PATCH] tw686x: use a formula instead of two tables for div Mauro Carvalho Chehab
@ 2016-04-27 12:00       ` Mauro Carvalho Chehab
  2016-04-27 15:17         ` [PATCH v2] [media] tw686x: cleanup the fps estimation code Mauro Carvalho Chehab
  2016-04-27 22:50         ` [PATCH] " Mauro Carvalho Chehab
  0 siblings, 2 replies; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-04-27 12:00 UTC (permalink / raw)
  To: Linux Media Mailing List; +Cc: Mauro Carvalho Chehab, Ezequiel Garcia

Hmm....

Em Wed, 27 Apr 2016 08:01:19 -0300
Mauro Carvalho Chehab <mchehab@osg.samsung.com> escreveu:

> Instead of using two tables to estimate the temporal decimation
> factor, use a formula. This allows to get the closest fps, with
> sounds better than the current tables.
> 
> Compile-tested only.

Please discard this patch. It is wrong.

I found the datasheet for this device at:
	http://www.starterkit.ru/html/doc/tw6869-ds.pdf

Based on what it is said on page 50, it seems that it doesn't use a
decimation filter, but, instead, it just discards some fields in
a way that the average fps will be reduced.

So, the actual frame rate is given by the number of enabled bits
that are written to VIDEO_FIELD_CTRL[vc->ch] and are at the
valid bitmask range, e. g:

index  0, map = 0x00000000, 30 fps (60Hz), 25 fps (50Hz), output all fields
index  1, map = 0x80000006, 2 fps (60Hz), 2 fps (50Hz)
index  2, map = 0x80018006, 4 fps (60Hz), 4 fps (50Hz)
index  3, map = 0x80618006, 6 fps (60Hz), 6 fps (50Hz)
index  4, map = 0x81818186, 8 fps (60Hz), 8 fps (50Hz)
index  5, map = 0x86186186, 10 fps (60Hz), 8 fps (50Hz)
index  6, map = 0x86619866, 12 fps (60Hz), 10 fps (50Hz)
index  7, map = 0x86666666, 14 fps (60Hz), 12 fps (50Hz)
index  8, map = 0x9999999e, 16 fps (60Hz), 14 fps (50Hz)
index  9, map = 0x99e6799e, 18 fps (60Hz), 16 fps (50Hz)
index 10, map = 0x9e79e79e, 20 fps (60Hz), 16 fps (50Hz)
index 11, map = 0x9e7e7e7e, 22 fps (60Hz), 18 fps (50Hz)
index 12, map = 0x9fe7f9fe, 24 fps (60Hz), 20 fps (50Hz)
index 13, map = 0x9ffe7ffe, 26 fps (60Hz), 22 fps (50Hz)
index 14, map = 0x9ffffffe, 28 fps (60Hz), 24 fps (50Hz)

This was done by using the enclosed program.

---

#include <stdio.h>

static const unsigned int map[15] = {
                0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041,
               	0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
                0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
};

unsigned int count_bits(unsigned int bits, unsigned int max)
{
	unsigned int i, c= 0;

	for (i = 0; i < max; i++)
		if ((1 << i) & bits)
			c++;

	return c;
}

void calc_map(unsigned int i)
{
	unsigned int m, fps_30, fps_25;

	m = map[i] << 1;
	m |= m << 1;

	fps_30 = count_bits(m, 30);
	fps_25 = count_bits(m, 25);

	if (m)
		m |= 1 << 31;
	else {
		fps_30 = 30;
		fps_25 = 25;
	}

	printf("index %2i, map = 0x%08x, %d fps (60Hz), %d fps (50Hz)%s\n",
		i, m, fps_30, fps_25,
		i == 0 ? ", output all fields" : ""
		);
}

int main(void)
{
	unsigned int i;

	printf ("\nmap:\n");
	for (i = 0; i < 15; i++)
		calc_map(i);

	return 0;
}

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v2] [media] tw686x: cleanup the fps estimation code
  2016-04-27 12:00       ` Mauro Carvalho Chehab
@ 2016-04-27 15:17         ` Mauro Carvalho Chehab
  2016-04-27 15:27           ` [PATCH v3] tw686x: use a formula instead of two tables for div Mauro Carvalho Chehab
  2016-04-27 22:50         ` [PATCH] " Mauro Carvalho Chehab
  1 sibling, 1 reply; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-04-27 15:17 UTC (permalink / raw)
  To: Linux Media Mailing List
  Cc: Mauro Carvalho Chehab, Mauro Carvalho Chehab, Ezequiel Garcia

There are some issues with the old code:
	1) it uses two static tables;
	2) some values for 50Hz standards are wrong;
	3) it doesn't store the real framerate.

This patch fixes the above issues.

Compile-tested only.

Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>

---

PS.: With this patch, it should be easy to add support for
VIDIOC_G_PARM and VIDIOC_S_PARM, as vc->fps will now store the
real frame rate, with should be used when returning from those
functions.

 drivers/media/pci/tw686x/tw686x-video.c | 100 +++++++++++++++++++++++---------
 1 file changed, 73 insertions(+), 27 deletions(-)

diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index 0210fa304e4c..b247a7b4ddd8 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -43,43 +43,89 @@ static const struct tw686x_format formats[] = {
 	}
 };
 
-static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
+static const unsigned int fps_map[15] = {
+	/*
+	 * bit 31 enables selecting the field control register
+	 * bits 0-29 are a bitmask with fields that will be output.
+	 * For NTSC (and PAL-M, PAL-60), all 30 bits are used.
+	 * For other PAL standards, only the first 25 bits are used.
+	 */
+	0x00000000, /* output all fields */
+	0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */
+	0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */
+	0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */
+	0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */
+	0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */
+	0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */
+	0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */
+	0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */
+	0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */
+	0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */
+	0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */
+	0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */
+	0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */
+	0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */
+};
+
+static unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps)
+{
+	unsigned int i, bits, c = 0;
+
+	if (!index || index >= ARRAY_SIZE(fps_map))
+		return max_fps;
+
+	bits = fps_map[index];
+	for (i = 0; i < max_fps; i++)
+		if ((1 << i) & bits)
+			c++;
+
+	return c;
+}
+
+static unsigned int tw686x_fps_idx(unsigned int fps, unsigned int max_fps)
 {
-	static const unsigned int map[15] = {
-		0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041,
-		0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
-		0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
-	};
-	unsigned int i, max_fps;
-
-	if (std & V4L2_STD_525_60)
-		max_fps = 30;
-	else
-		max_fps = 25;
-
-	i = DIV_ROUND_CLOSEST(15 * fps, max_fps);
-	if (!i)
-		i = 1;	/* Min possible fps */
-	else if (i > 14)
-		i = 0;	/* fps = max_fps */
-
-	return map[i];
+	unsigned int idx, real_fps;
+	int delta;
+
+	/* First guess */
+	idx = (12 + 15 * fps) / max_fps;
+
+	/* Minimal possible framerate is 2 frames per second */
+	if (!idx)
+		return 1;
+
+	/* Check if the difference is bigger than abs(1) and adjust */
+	real_fps = tw686x_real_fps(idx, max_fps);
+	delta = real_fps - fps;
+	if (delta < -1)
+		idx++;
+	else if (delta > 1)
+		idx--;
+
+	/* Max framerate */
+	if (idx >= 15)
+		return 0;
+
+	return idx;
 }
 
 static void tw686x_set_framerate(struct tw686x_video_channel *vc,
 				 unsigned int fps)
 {
-	unsigned int map;
+	unsigned int i, max_fps;
 
 	if (vc->fps == fps)
 		return;
 
-	map = tw686x_fields_map(vc->video_standard, fps) << 1;
-	map |= map << 1;
-	if (map > 0)
-		map |= BIT(31);
-	reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], map);
-	vc->fps = fps;
+	if (vc->video_standard & V4L2_STD_525_60)
+		max_fps = 30;
+	else
+		max_fps = 25;
+
+	i = tw686x_fps_idx(fps, max_fps);
+
+	reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], fps_map[i]);
+	vc->fps = tw686x_real_fps(i, max_fps);
 }
 
 static const struct tw686x_format *format_by_fourcc(unsigned int fourcc)
-- 
2.5.5



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v3] tw686x: use a formula instead of two tables for div
  2016-04-27 15:17         ` [PATCH v2] [media] tw686x: cleanup the fps estimation code Mauro Carvalho Chehab
@ 2016-04-27 15:27           ` Mauro Carvalho Chehab
  2016-06-27 15:45             ` Ezequiel Garcia
  0 siblings, 1 reply; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-04-27 15:27 UTC (permalink / raw)
  To: Linux Media Mailing List
  Cc: Mauro Carvalho Chehab, Mauro Carvalho Chehab, Ezequiel Garcia

Instead of using two tables to estimate the temporal decimation
factor, use a formula. This allows to get the closest fps, with
sounds better than the current tables.

Compile-tested only.

Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>

[media] tw686x: cleanup the fps estimation code

There are some issues with the old code:
	1) it uses two static tables;
	2) some values for 50Hz standards are wrong;
	3) it doesn't store the real framerate.

This patch fixes the above issues.

Compile-tested only.

Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>

-

v3: Patch v2 were actually a diff patch against PATCH v1. Fold the two patches in one.

PS.: With this patch, it should be easy to add support for
VIDIOC_G_PARM and VIDIOC_S_PARM, as vc->fps will now store the
real frame rate, with should be used when returning from those
functions.

---
 drivers/media/pci/tw686x/tw686x-video.c | 110 +++++++++++++++++++++-----------
 1 file changed, 73 insertions(+), 37 deletions(-)

diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
index 253e10823ba3..b247a7b4ddd8 100644
--- a/drivers/media/pci/tw686x/tw686x-video.c
+++ b/drivers/media/pci/tw686x/tw686x-video.c
@@ -43,53 +43,89 @@ static const struct tw686x_format formats[] = {
 	}
 };
 
-static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
+static const unsigned int fps_map[15] = {
+	/*
+	 * bit 31 enables selecting the field control register
+	 * bits 0-29 are a bitmask with fields that will be output.
+	 * For NTSC (and PAL-M, PAL-60), all 30 bits are used.
+	 * For other PAL standards, only the first 25 bits are used.
+	 */
+	0x00000000, /* output all fields */
+	0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */
+	0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */
+	0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */
+	0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */
+	0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */
+	0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */
+	0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */
+	0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */
+	0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */
+	0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */
+	0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */
+	0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */
+	0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */
+	0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */
+};
+
+static unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps)
+{
+	unsigned int i, bits, c = 0;
+
+	if (!index || index >= ARRAY_SIZE(fps_map))
+		return max_fps;
+
+	bits = fps_map[index];
+	for (i = 0; i < max_fps; i++)
+		if ((1 << i) & bits)
+			c++;
+
+	return c;
+}
+
+static unsigned int tw686x_fps_idx(unsigned int fps, unsigned int max_fps)
 {
-	static const unsigned int map[15] = {
-		0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041,
-		0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
-		0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
-	};
-
-	static const unsigned int std_625_50[26] = {
-		0, 1, 1, 2,  3,  3,  4,  4,  5,  5,  6,  7,  7,
-		   8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 0
-	};
-
-	static const unsigned int std_525_60[31] = {
-		0, 1, 1, 1, 2,  2,  3,  3,  4,  4,  5,  5,  6,  6, 7, 7,
-		   8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0
-	};
-
-	unsigned int i;
-
-	if (std & V4L2_STD_525_60) {
-		if (fps >= ARRAY_SIZE(std_525_60))
-			fps = 30;
-		i = std_525_60[fps];
-	} else {
-		if (fps >= ARRAY_SIZE(std_625_50))
-			fps = 25;
-		i = std_625_50[fps];
-	}
-
-	return map[i];
+	unsigned int idx, real_fps;
+	int delta;
+
+	/* First guess */
+	idx = (12 + 15 * fps) / max_fps;
+
+	/* Minimal possible framerate is 2 frames per second */
+	if (!idx)
+		return 1;
+
+	/* Check if the difference is bigger than abs(1) and adjust */
+	real_fps = tw686x_real_fps(idx, max_fps);
+	delta = real_fps - fps;
+	if (delta < -1)
+		idx++;
+	else if (delta > 1)
+		idx--;
+
+	/* Max framerate */
+	if (idx >= 15)
+		return 0;
+
+	return idx;
 }
 
 static void tw686x_set_framerate(struct tw686x_video_channel *vc,
 				 unsigned int fps)
 {
-	unsigned int map;
+	unsigned int i, max_fps;
 
 	if (vc->fps == fps)
 		return;
 
-	map = tw686x_fields_map(vc->video_standard, fps) << 1;
-	map |= map << 1;
-	if (map > 0)
-		map |= BIT(31);
-	reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], map);
-	vc->fps = fps;
+	if (vc->video_standard & V4L2_STD_525_60)
+		max_fps = 30;
+	else
+		max_fps = 25;
+
+	i = tw686x_fps_idx(fps, max_fps);
+
+	reg_write(vc->dev, VIDEO_FIELD_CTRL[vc->ch], fps_map[i]);
+	vc->fps = tw686x_real_fps(i, max_fps);
 }
 
 static const struct tw686x_format *format_by_fourcc(unsigned int fourcc)
-- 
2.5.5



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] tw686x: use a formula instead of two tables for div
  2016-04-27 12:00       ` Mauro Carvalho Chehab
  2016-04-27 15:17         ` [PATCH v2] [media] tw686x: cleanup the fps estimation code Mauro Carvalho Chehab
@ 2016-04-27 22:50         ` Mauro Carvalho Chehab
  1 sibling, 0 replies; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-04-27 22:50 UTC (permalink / raw)
  To: Ezequiel Garcia; +Cc: Linux Media Mailing List

Em Wed, 27 Apr 2016 09:00:49 -0300
Mauro Carvalho Chehab <mchehab@osg.samsung.com> escreveu:

> Hmm....
> 
> Em Wed, 27 Apr 2016 08:01:19 -0300
> Mauro Carvalho Chehab <mchehab@osg.samsung.com> escreveu:
> 
> > Instead of using two tables to estimate the temporal decimation
> > factor, use a formula. This allows to get the closest fps, with
> > sounds better than the current tables.
> > 
> > Compile-tested only.  
> 
> Please discard this patch. It is wrong.
> 
> I found the datasheet for this device at:
> 	http://www.starterkit.ru/html/doc/tw6869-ds.pdf
> 
> Based on what it is said on page 50, it seems that it doesn't use a
> decimation filter, but, instead, it just discards some fields in
> a way that the average fps will be reduced.
> 
> So, the actual frame rate is given by the number of enabled bits
> that are written to VIDEO_FIELD_CTRL[vc->ch]

Ok, I think I got this right this time. See the enclosed code.

It produces the fps register map, with each fps associated to
both 60Hz and 50Hz standard, plus it replaces the tables by a
calculus.

If my code is right, there are some values on the current tables
that are wrong.

See below. I'll submit an updated patch soon.

---
Program results:

FPS map:
	index  0, map = 0x00000000, 30 fps (60Hz), 25 fps (50Hz), output all fields
	index  1, map = 0x80000006, 2 fps (60Hz), 2 fps (50Hz)
	index  2, map = 0x80018006, 4 fps (60Hz), 4 fps (50Hz)
	index  3, map = 0x80618006, 6 fps (60Hz), 6 fps (50Hz)
	index  4, map = 0x81818186, 8 fps (60Hz), 8 fps (50Hz)
	index  5, map = 0x86186186, 10 fps (60Hz), 8 fps (50Hz)
	index  6, map = 0x86619866, 12 fps (60Hz), 10 fps (50Hz)
	index  7, map = 0x86666666, 14 fps (60Hz), 12 fps (50Hz)
	index  8, map = 0x9999999e, 16 fps (60Hz), 14 fps (50Hz)
	index  9, map = 0x99e6799e, 18 fps (60Hz), 16 fps (50Hz)
	index 10, map = 0x9e79e79e, 20 fps (60Hz), 16 fps (50Hz)
	index 11, map = 0x9e7e7e7e, 22 fps (60Hz), 18 fps (50Hz)
	index 12, map = 0x9fe7f9fe, 24 fps (60Hz), 20 fps (50Hz)
	index 13, map = 0x9ffe7ffe, 26 fps (60Hz), 22 fps (50Hz)
	index 14, map = 0x9ffffffe, 28 fps (60Hz), 24 fps (50Hz)

60 Hz
	Requested fps 0, table 0 (30 fps, delta 30), calculus 1 (2 fps, delta 2) DIFFERENT!
	Requested fps 1, table 1 (2 fps, delta 1), calculus 1 (2 fps, delta 1)
	Requested fps 2, table 1 (2 fps, delta 0), calculus 1 (2 fps, delta 0)
	Requested fps 3, table 1 (2 fps, delta -1), calculus 1 (2 fps, delta -1)
	Requested fps 4, table 2 (4 fps, delta 0), calculus 2 (4 fps, delta 0)
	Requested fps 5, table 2 (4 fps, delta -1), calculus 2 (4 fps, delta -1)
	Requested fps 6, table 3 (6 fps, delta 0), calculus 3 (6 fps, delta 0)
	Requested fps 7, table 3 (6 fps, delta -1), calculus 3 (6 fps, delta -1)
	Requested fps 8, table 4 (8 fps, delta 0), calculus 4 (8 fps, delta 0)
	Requested fps 9, table 4 (8 fps, delta -1), calculus 4 (8 fps, delta -1)
	Requested fps 10, table 5 (10 fps, delta 0), calculus 5 (10 fps, delta 0)
	Requested fps 11, table 5 (10 fps, delta -1), calculus 5 (10 fps, delta -1)
	Requested fps 12, table 6 (12 fps, delta 0), calculus 6 (12 fps, delta 0)
	Requested fps 13, table 6 (12 fps, delta -1), calculus 6 (12 fps, delta -1)
	Requested fps 14, table 7 (14 fps, delta 0), calculus 7 (14 fps, delta 0)
	Requested fps 15, table 7 (14 fps, delta -1), calculus 7 (14 fps, delta -1)
	Requested fps 16, table 8 (16 fps, delta 0), calculus 8 (16 fps, delta 0)
	Requested fps 17, table 8 (16 fps, delta -1), calculus 8 (16 fps, delta -1)
	Requested fps 18, table 9 (18 fps, delta 0), calculus 9 (18 fps, delta 0)
	Requested fps 19, table 9 (18 fps, delta -1), calculus 9 (18 fps, delta -1)
	Requested fps 20, table 10 (20 fps, delta 0), calculus 10 (20 fps, delta 0)
	Requested fps 21, table 10 (20 fps, delta -1), calculus 10 (20 fps, delta -1)
	Requested fps 22, table 11 (22 fps, delta 0), calculus 11 (22 fps, delta 0)
	Requested fps 23, table 11 (22 fps, delta -1), calculus 11 (22 fps, delta -1)
	Requested fps 24, table 12 (24 fps, delta 0), calculus 12 (24 fps, delta 0)
	Requested fps 25, table 12 (24 fps, delta -1), calculus 12 (24 fps, delta -1)
	Requested fps 26, table 13 (26 fps, delta 0), calculus 13 (26 fps, delta 0)
	Requested fps 27, table 13 (26 fps, delta -1), calculus 13 (26 fps, delta -1)
	Requested fps 28, table 14 (28 fps, delta 0), calculus 14 (28 fps, delta 0)
	Requested fps 29, table 0 (30 fps, delta 1), calculus 14 (28 fps, delta -1) DIFFERENT!
	Requested fps 30, table 0 (30 fps, delta 0), calculus 0 (30 fps, delta 0)

50 Hz
	Requested fps 0, table 0 (25 fps, delta 25), calculus 1 (2 fps, delta 2) DIFFERENT!
	Requested fps 1, table 1 (2 fps, delta 1), calculus 1 (2 fps, delta 1)
	Requested fps 2, table 1 (2 fps, delta 0), calculus 1 (2 fps, delta 0)
	Requested fps 3, table 2 (4 fps, delta 1), calculus 2 (4 fps, delta 1)
	Requested fps 4, table 3 (6 fps, delta 2), calculus 2 (4 fps, delta 0) DIFFERENT!
	Requested fps 5, table 3 (6 fps, delta 1), calculus 3 (6 fps, delta 1)
	Requested fps 6, table 4 (8 fps, delta 2), calculus 3 (6 fps, delta 0) DIFFERENT!
	Requested fps 7, table 4 (8 fps, delta 1), calculus 4 (8 fps, delta 1)
	Requested fps 8, table 5 (8 fps, delta 0), calculus 5 (8 fps, delta 0)
	Requested fps 9, table 5 (8 fps, delta -1), calculus 5 (8 fps, delta -1)
	Requested fps 10, table 6 (10 fps, delta 0), calculus 6 (10 fps, delta 0)
	Requested fps 11, table 7 (12 fps, delta 1), calculus 7 (12 fps, delta 1)
	Requested fps 12, table 7 (12 fps, delta 0), calculus 7 (12 fps, delta 0)
	Requested fps 13, table 8 (14 fps, delta 1), calculus 8 (14 fps, delta 1)
	Requested fps 14, table 8 (14 fps, delta 0), calculus 8 (14 fps, delta 0)
	Requested fps 15, table 9 (16 fps, delta 1), calculus 9 (16 fps, delta 1)
	Requested fps 16, table 10 (16 fps, delta 0), calculus 10 (16 fps, delta 0)
	Requested fps 17, table 10 (16 fps, delta -1), calculus 10 (16 fps, delta -1)
	Requested fps 18, table 11 (18 fps, delta 0), calculus 11 (18 fps, delta 0)
	Requested fps 19, table 11 (18 fps, delta -1), calculus 11 (18 fps, delta -1)
	Requested fps 20, table 12 (20 fps, delta 0), calculus 12 (20 fps, delta 0)
	Requested fps 21, table 13 (22 fps, delta 1), calculus 13 (22 fps, delta 1)
	Requested fps 22, table 13 (22 fps, delta 0), calculus 13 (22 fps, delta 0)
	Requested fps 23, table 14 (24 fps, delta 1), calculus 14 (24 fps, delta 1)
	Requested fps 24, table 14 (24 fps, delta 0), calculus 14 (24 fps, delta 0)
	Requested fps 25, table 0 (25 fps, delta 0), calculus 0 (25 fps, delta 0)

---

Program:

#include <stdio.h>

static const unsigned int std_625_50[26] = {
                0, 1, 1, 2,  3,  3,  4,  4,  5,  5,  6,  7,  7,
                   8, 8, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 0
};

static const unsigned int std_525_60[31] = {
                0, 1, 1, 1, 2,  2,  3,  3,  4,  4,  5,  5,  6,  6, 7, 7,
                   8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 0, 0
};

static const unsigned int map[15] = {
                0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041,
               	0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
                0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
};

unsigned int real_rate(unsigned int index, unsigned int max)
{
	unsigned int i, bits, c = 0;

	if (!index)
		return max;

	bits = map[index] << 1;
	bits |= bits << 1;

	for (i = 0; i < max; i++)
		if ((1 << i) & bits)
			c++;

	return c;
}

void calc_map(unsigned int i)
{
	unsigned int m, fps_30, fps_25;

	fps_30 = real_rate(i, 30);
	fps_25 = real_rate(i, 25);

	m = map[i] << 1;
	m |= m << 1;

	if (m)
		m |= 1 << 31;
	else {
		fps_30 = 30;
		fps_25 = 25;
	}

	printf("\tindex %2i, map = 0x%08x, %d fps (60Hz), %d fps (50Hz)%s\n",
		i, m, fps_30, fps_25,
		i == 0 ? ", output all fields" : ""
		);
}

unsigned int get_index(unsigned int fps, unsigned int max_fps)
{
	unsigned int idx, real_fps;
	int delta;

	if (real_fps == max_fps)
		return 0;

	if (fps < 2)
		return 1;

	/* First guess */
	idx = (12 + 15 * fps) / max_fps;

	/* Check if the difference is bigger than abs(1) and adjust */
	real_fps = real_rate(idx, max_fps);
	delta = real_fps - fps;
	if (delta < -1)
		idx++;
	else if (delta > 1)
		idx--;

	if (idx >= 15)
		return 0;

	return idx;
}


void calc_fps(unsigned int max_fps)
{
	unsigned int i1, i2, fps, adjust;
	unsigned int fps1, fps2;

	for (fps = 0; fps <= max_fps; fps++) {
		if (max_fps == 30)
			i1 = std_525_60[fps];
		else
			i1 = std_625_50[fps];

		i2 = get_index(fps, max_fps);

		fps1 = real_rate(i1, max_fps);
		fps2 = real_rate(i2, max_fps);

		printf("\tRequested fps %d, table %d (%d fps, delta %d), calculus %d (%d fps, delta %d)%s\n",
			fps,
			i1, fps1, fps1 - fps,
			i2, fps2, fps2 - fps,
			(i1 != i2) ? " DIFFERENT!": "");
	}
}

int main(void)
{
	unsigned int i;

	printf ("FPS map:\n");
	for (i = 0; i < 15; i++)
		calc_map(i);

	printf ("\n60 Hz\n");
	calc_fps(30);

	printf ("\n50 Hz\n");
	calc_fps(25);

	return 0;
}

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3] tw686x: use a formula instead of two tables for div
  2016-04-27 15:27           ` [PATCH v3] tw686x: use a formula instead of two tables for div Mauro Carvalho Chehab
@ 2016-06-27 15:45             ` Ezequiel Garcia
  2016-06-27 17:38               ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 12+ messages in thread
From: Ezequiel Garcia @ 2016-06-27 15:45 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Media Mailing List, Mauro Carvalho Chehab

Hi Mauro,

Thanks a lot for the patch.

On 27 April 2016 at 12:27, Mauro Carvalho Chehab
<mchehab@osg.samsung.com> wrote:
> Instead of using two tables to estimate the temporal decimation
> factor, use a formula. This allows to get the closest fps, with
> sounds better than the current tables.
>
> Compile-tested only.
>
> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
>
> [media] tw686x: cleanup the fps estimation code
>
> There are some issues with the old code:
>         1) it uses two static tables;
>         2) some values for 50Hz standards are wrong;
>         3) it doesn't store the real framerate.
>
> This patch fixes the above issues.
>
> Compile-tested only.
>
> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
>
> -
>
> v3: Patch v2 were actually a diff patch against PATCH v1. Fold the two patches in one.
>
> PS.: With this patch, it should be easy to add support for
> VIDIOC_G_PARM and VIDIOC_S_PARM, as vc->fps will now store the
> real frame rate, with should be used when returning from those
> functions.
>
> ---
>  drivers/media/pci/tw686x/tw686x-video.c | 110 +++++++++++++++++++++-----------
>  1 file changed, 73 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
> index 253e10823ba3..b247a7b4ddd8 100644
> --- a/drivers/media/pci/tw686x/tw686x-video.c
> +++ b/drivers/media/pci/tw686x/tw686x-video.c
> @@ -43,53 +43,89 @@ static const struct tw686x_format formats[] = {
>         }
>  };
>
> -static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
> +static const unsigned int fps_map[15] = {
> +       /*
> +        * bit 31 enables selecting the field control register
> +        * bits 0-29 are a bitmask with fields that will be output.
> +        * For NTSC (and PAL-M, PAL-60), all 30 bits are used.
> +        * For other PAL standards, only the first 25 bits are used.
> +        */

I ran a few tests and it worked perfectly fine for 60Hz standards.
For 50Hz standards, or at least for PAL-Nc, it didn't
work so well, and the real FPS was too different from the requested
one. I need to look into it some more.

> +       0x00000000, /* output all fields */
> +       0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */
> +       0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */
> +       0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */
> +       0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */
> +       0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */
> +       0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */
> +       0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */
> +       0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */
> +       0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */
> +       0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */
> +       0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */
> +       0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */
> +       0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */
> +       0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */

Why this particular selection of fps values and bits set in each case?
Is it arbitrary?

> +};
> +
> +static unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps)
> +{
> +       unsigned int i, bits, c = 0;
> +
> +       if (!index || index >= ARRAY_SIZE(fps_map))
> +               return max_fps;
> +
> +       bits = fps_map[index];
> +       for (i = 0; i < max_fps; i++)
> +               if ((1 << i) & bits)
> +                       c++;
> +

We can use hweight_long here to count the number of bits set.
If you are OK with it, I can rework the patch and submit a new version.
-- 
Ezequiel García, VanguardiaSur
www.vanguardiasur.com.ar

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3] tw686x: use a formula instead of two tables for div
  2016-06-27 15:45             ` Ezequiel Garcia
@ 2016-06-27 17:38               ` Mauro Carvalho Chehab
  2016-06-29  0:11                 ` Ezequiel Garcia
  0 siblings, 1 reply; 12+ messages in thread
From: Mauro Carvalho Chehab @ 2016-06-27 17:38 UTC (permalink / raw)
  To: Ezequiel Garcia; +Cc: Linux Media Mailing List, Mauro Carvalho Chehab

Em Mon, 27 Jun 2016 12:45:38 -0300
Ezequiel Garcia <ezequiel@vanguardiasur.com.ar> escreveu:

> Hi Mauro,
> 
> Thanks a lot for the patch.
> 
> On 27 April 2016 at 12:27, Mauro Carvalho Chehab
> <mchehab@osg.samsung.com> wrote:
> > Instead of using two tables to estimate the temporal decimation
> > factor, use a formula. This allows to get the closest fps, with
> > sounds better than the current tables.
> >
> > Compile-tested only.
> >
> > Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
> >
> > [media] tw686x: cleanup the fps estimation code
> >
> > There are some issues with the old code:
> >         1) it uses two static tables;
> >         2) some values for 50Hz standards are wrong;
> >         3) it doesn't store the real framerate.
> >
> > This patch fixes the above issues.
> >
> > Compile-tested only.
> >
> > Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
> >
> > -
> >
> > v3: Patch v2 were actually a diff patch against PATCH v1. Fold the two patches in one.
> >
> > PS.: With this patch, it should be easy to add support for
> > VIDIOC_G_PARM and VIDIOC_S_PARM, as vc->fps will now store the
> > real frame rate, with should be used when returning from those
> > functions.
> >
> > ---
> >  drivers/media/pci/tw686x/tw686x-video.c | 110 +++++++++++++++++++++-----------
> >  1 file changed, 73 insertions(+), 37 deletions(-)
> >
> > diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
> > index 253e10823ba3..b247a7b4ddd8 100644
> > --- a/drivers/media/pci/tw686x/tw686x-video.c
> > +++ b/drivers/media/pci/tw686x/tw686x-video.c
> > @@ -43,53 +43,89 @@ static const struct tw686x_format formats[] = {
> >         }
> >  };
> >
> > -static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
> > +static const unsigned int fps_map[15] = {
> > +       /*
> > +        * bit 31 enables selecting the field control register
> > +        * bits 0-29 are a bitmask with fields that will be output.
> > +        * For NTSC (and PAL-M, PAL-60), all 30 bits are used.
> > +        * For other PAL standards, only the first 25 bits are used.
> > +        */  
> 
> I ran a few tests and it worked perfectly fine for 60Hz standards.

Good!

> For 50Hz standards, or at least for PAL-Nc, it didn't
> work so well, and the real FPS was too different from the requested
> one. I need to look into it some more.

I would be expecting a maximum difference a little bigger than 1 Hz.

> > +       0x00000000, /* output all fields */
> > +       0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */
> > +       0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */
> > +       0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */
> > +       0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */
> > +       0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */
> > +       0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */
> > +       0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */
> > +       0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */
> > +       0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */
> > +       0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */
> > +       0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */
> > +       0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */
> > +       0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */
> > +       0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */  
> 
> Why this particular selection of fps values and bits set in each case?
> Is it arbitrary?

No. This is the same table that was already in the code:

static const unsigned int map[15] = {
                0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041,
                0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
                0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
};

Except that the calculus that used to be there to set bit 31 to 1
on everything except map[0] and the code that makes it set two
FPS at the same time were pre-calculated, e. g. I run this code
locally to generate the new table:

	map = tw686x_fields_map(vc->video_standard, fps) << 1;
	map |= map << 1;
	if (map > 0)
		map |= BIT(31);

There, bit 31 = 0 disables the frame filtering. bit 31 = 1 enables it.

Each bit at the 0-29 bitrange means one of the 30 frames received.
If equal to 1, the frame is sent; if equal to 0, it is not sent.

So, the first value, for example: 0x80000006 has 2 consecutive
bits set, so the mean frame rate would be 2Hz. The next value there
is 0x0x80018006, with has 4 bits selected, so mean fps is 4Hz, and
so on.

As the original table always sets 2 consecutive frames at the same
time, I suspect that this is a requirement to avoid interlacing
issues.

So, the code I used to generate the table always allocate 2 consecutive
bits each time.

I suspect that such the table was built assuming that there are 30 bits,
but, on 50Hz, only 25 bits are used. So, it is sub-optimal for 50 Hz.
It means that the frames are not equally spaced.

If you want, I can construct a table for 50Hz that would produce better
results.

> 
> > +};
> > +
> > +static unsigned int tw686x_real_fps(unsigned int index, unsigned int max_fps)
> > +{
> > +       unsigned int i, bits, c = 0;
> > +
> > +       if (!index || index >= ARRAY_SIZE(fps_map))
> > +               return max_fps;
> > +
> > +       bits = fps_map[index];
> > +       for (i = 0; i < max_fps; i++)
> > +               if ((1 << i) & bits)
> > +                       c++;
> > +  
> 
> We can use hweight_long here to count the number of bits set.
> If you are OK with it, I can rework the patch and submit a new version.


-- 
Thanks,
Mauro

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v3] tw686x: use a formula instead of two tables for div
  2016-06-27 17:38               ` Mauro Carvalho Chehab
@ 2016-06-29  0:11                 ` Ezequiel Garcia
  0 siblings, 0 replies; 12+ messages in thread
From: Ezequiel Garcia @ 2016-06-29  0:11 UTC (permalink / raw)
  To: Mauro Carvalho Chehab; +Cc: Linux Media Mailing List, Mauro Carvalho Chehab

On 27 June 2016 at 14:38, Mauro Carvalho Chehab <mchehab@osg.samsung.com> wrote:
> Em Mon, 27 Jun 2016 12:45:38 -0300
> Ezequiel Garcia <ezequiel@vanguardiasur.com.ar> escreveu:
>
>> Hi Mauro,
>>
>> Thanks a lot for the patch.
>>
>> On 27 April 2016 at 12:27, Mauro Carvalho Chehab
>> <mchehab@osg.samsung.com> wrote:
>> > Instead of using two tables to estimate the temporal decimation
>> > factor, use a formula. This allows to get the closest fps, with
>> > sounds better than the current tables.
>> >
>> > Compile-tested only.
>> >
>> > Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
>> >
>> > [media] tw686x: cleanup the fps estimation code
>> >
>> > There are some issues with the old code:
>> >         1) it uses two static tables;
>> >         2) some values for 50Hz standards are wrong;
>> >         3) it doesn't store the real framerate.
>> >
>> > This patch fixes the above issues.
>> >
>> > Compile-tested only.
>> >
>> > Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
>> >
>> > -
>> >
>> > v3: Patch v2 were actually a diff patch against PATCH v1. Fold the two patches in one.
>> >
>> > PS.: With this patch, it should be easy to add support for
>> > VIDIOC_G_PARM and VIDIOC_S_PARM, as vc->fps will now store the
>> > real frame rate, with should be used when returning from those
>> > functions.
>> >
>> > ---
>> >  drivers/media/pci/tw686x/tw686x-video.c | 110 +++++++++++++++++++++-----------
>> >  1 file changed, 73 insertions(+), 37 deletions(-)
>> >
>> > diff --git a/drivers/media/pci/tw686x/tw686x-video.c b/drivers/media/pci/tw686x/tw686x-video.c
>> > index 253e10823ba3..b247a7b4ddd8 100644
>> > --- a/drivers/media/pci/tw686x/tw686x-video.c
>> > +++ b/drivers/media/pci/tw686x/tw686x-video.c
>> > @@ -43,53 +43,89 @@ static const struct tw686x_format formats[] = {
>> >         }
>> >  };
>> >
>> > -static unsigned int tw686x_fields_map(v4l2_std_id std, unsigned int fps)
>> > +static const unsigned int fps_map[15] = {
>> > +       /*
>> > +        * bit 31 enables selecting the field control register
>> > +        * bits 0-29 are a bitmask with fields that will be output.
>> > +        * For NTSC (and PAL-M, PAL-60), all 30 bits are used.
>> > +        * For other PAL standards, only the first 25 bits are used.
>> > +        */
>>
>> I ran a few tests and it worked perfectly fine for 60Hz standards.
>
> Good!
>
>> For 50Hz standards, or at least for PAL-Nc, it didn't
>> work so well, and the real FPS was too different from the requested
>> one. I need to look into it some more.
>
> I would be expecting a maximum difference a little bigger than 1 Hz.
>
>> > +       0x00000000, /* output all fields */
>> > +       0x80000006, /* 2 fps (60Hz), 2 fps (50Hz) */
>> > +       0x80018006, /* 4 fps (60Hz), 4 fps (50Hz) */
>> > +       0x80618006, /* 6 fps (60Hz), 6 fps (50Hz) */
>> > +       0x81818186, /* 8 fps (60Hz), 8 fps (50Hz) */
>> > +       0x86186186, /* 10 fps (60Hz), 8 fps (50Hz) */
>> > +       0x86619866, /* 12 fps (60Hz), 10 fps (50Hz) */
>> > +       0x86666666, /* 14 fps (60Hz), 12 fps (50Hz) */
>> > +       0x9999999e, /* 16 fps (60Hz), 14 fps (50Hz) */
>> > +       0x99e6799e, /* 18 fps (60Hz), 16 fps (50Hz) */
>> > +       0x9e79e79e, /* 20 fps (60Hz), 16 fps (50Hz) */
>> > +       0x9e7e7e7e, /* 22 fps (60Hz), 18 fps (50Hz) */
>> > +       0x9fe7f9fe, /* 24 fps (60Hz), 20 fps (50Hz) */
>> > +       0x9ffe7ffe, /* 26 fps (60Hz), 22 fps (50Hz) */
>> > +       0x9ffffffe, /* 28 fps (60Hz), 24 fps (50Hz) */
>>
>> Why this particular selection of fps values and bits set in each case?
>> Is it arbitrary?
>
> No. This is the same table that was already in the code:
>
> static const unsigned int map[15] = {
>                 0x00000000, 0x00000001, 0x00004001, 0x00104001, 0x00404041,
>                 0x01041041, 0x01104411, 0x01111111, 0x04444445, 0x04511445,
>                 0x05145145, 0x05151515, 0x05515455, 0x05551555, 0x05555555
> };
>
> Except that the calculus that used to be there to set bit 31 to 1
> on everything except map[0] and the code that makes it set two
> FPS at the same time were pre-calculated, e. g. I run this code
> locally to generate the new table:
>
>         map = tw686x_fields_map(vc->video_standard, fps) << 1;
>         map |= map << 1;
>         if (map > 0)
>                 map |= BIT(31);
>
> There, bit 31 = 0 disables the frame filtering. bit 31 = 1 enables it.
>
> Each bit at the 0-29 bitrange means one of the 30 frames received.
> If equal to 1, the frame is sent; if equal to 0, it is not sent.
>
> So, the first value, for example: 0x80000006 has 2 consecutive
> bits set, so the mean frame rate would be 2Hz. The next value there
> is 0x0x80018006, with has 4 bits selected, so mean fps is 4Hz, and
> so on.
>
> As the original table always sets 2 consecutive frames at the same
> time, I suspect that this is a requirement to avoid interlacing
> issues.
>
> So, the code I used to generate the table always allocate 2 consecutive
> bits each time.
>
> I suspect that such the table was built assuming that there are 30 bits,
> but, on 50Hz, only 25 bits are used. So, it is sub-optimal for 50 Hz.
> It means that the frames are not equally spaced.
>
> If you want, I can construct a table for 50Hz that would produce better
> results.
>

Actually, it's working fine on both 50Hz and 60Hz standards. I tested
PAL-Nc using my modifications, and I had an small error in the
hweight_long usage. My bad!

So... now I think it's working properly, for 50Hz the requested FPS matches
the actual FPS, and for 60Hz there's a slight difference:

* 24 FPS gives 23.50 FPS
* 22 FPS gives 21.66 FPS
* 20 FPS gives 20 FPS
* 10 FPS gives 10 FPS
* 6 FPS give 5 FPS
* 2 FPS gives 1.66 FPS

Output FPS values are those measured by qv4l2.
-- 
Ezequiel García, VanguardiaSur
www.vanguardiasur.com.ar

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2016-06-29  0:11 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-04-27  8:09 [patch] [media] tw686x: off by one bugs in tw686x_fields_map() Dan Carpenter
2016-04-27 10:31 ` Mauro Carvalho Chehab
2016-04-27 10:36   ` Dan Carpenter
2016-04-27 10:40   ` Mauro Carvalho Chehab
2016-04-27 11:01     ` [PATCH] tw686x: use a formula instead of two tables for div Mauro Carvalho Chehab
2016-04-27 12:00       ` Mauro Carvalho Chehab
2016-04-27 15:17         ` [PATCH v2] [media] tw686x: cleanup the fps estimation code Mauro Carvalho Chehab
2016-04-27 15:27           ` [PATCH v3] tw686x: use a formula instead of two tables for div Mauro Carvalho Chehab
2016-06-27 15:45             ` Ezequiel Garcia
2016-06-27 17:38               ` Mauro Carvalho Chehab
2016-06-29  0:11                 ` Ezequiel Garcia
2016-04-27 22:50         ` [PATCH] " Mauro Carvalho Chehab

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).