V4L/DVB: gspca - sn9c20x: Add upside down detection

Add support for detecting webcams that are mounted
upside down in laptops. Currently the only two known
are two MSI modesl using the 0c45:624f.

Signed-off-by: Brian Johnson <brijohn@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index 825d5b8..06c75db 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -27,6 +27,7 @@
 #include "jpeg.h"
 
 #include <media/v4l2-chip-ident.h>
+#include <linux/dmi.h>
 
 MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, "
 		"microdia project <microdia@googlegroups.com>");
@@ -55,6 +56,7 @@
 /* camera flags */
 #define HAS_BUTTON	0x1
 #define LED_REVERSE	0x2 /* some cameras unset gpio to turn on leds */
+#define FLIP_DETECT	0x4
 
 /* specific webcam descriptor */
 struct sd {
@@ -127,6 +129,25 @@
 static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val);
 static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val);
 
+static const struct dmi_system_id flip_dmi_table[] = {
+	{
+		.ident = "MSI MS-1034",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD."),
+			DMI_MATCH(DMI_PRODUCT_NAME, "MS-1034"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "0341")
+		}
+	},
+	{
+		.ident = "MSI MS-1632",
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
+			DMI_MATCH(DMI_BOARD_NAME, "MS-1632")
+		}
+	},
+	{}
+};
+
 static const struct ctrl sd_ctrls[] = {
 	{
 #define BRIGHTNESS_IDX 0
@@ -1496,17 +1517,26 @@
 
 static int set_hvflip(struct gspca_dev *gspca_dev)
 {
-	u8 value, tslb;
+	u8 value, tslb, hflip, vflip;
 	u16 value2;
 	struct sd *sd = (struct sd *) gspca_dev;
+
+	if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
+		hflip = !sd->hflip;
+		vflip = !sd->vflip;
+	} else {
+		hflip = sd->hflip;
+		vflip = sd->vflip;
+	}
+
 	switch (sd->sensor) {
 	case SENSOR_OV9650:
 		i2c_r1(gspca_dev, 0x1e, &value);
 		value &= ~0x30;
 		tslb = 0x01;
-		if (sd->hflip)
+		if (hflip)
 			value |= 0x20;
-		if (sd->vflip) {
+		if (vflip) {
 			value |= 0x10;
 			tslb = 0x49;
 		}
@@ -1517,9 +1547,9 @@
 	case SENSOR_MT9V011:
 		i2c_r2(gspca_dev, 0x20, &value2);
 		value2 &= ~0xc0a0;
-		if (sd->hflip)
+		if (hflip)
 			value2 |= 0x8080;
-		if (sd->vflip)
+		if (vflip)
 			value2 |= 0x4020;
 		i2c_w2(gspca_dev, 0x20, value2);
 		break;
@@ -1527,18 +1557,18 @@
 	case SENSOR_MT9V112:
 		i2c_r2(gspca_dev, 0x20, &value2);
 		value2 &= ~0x0003;
-		if (sd->hflip)
+		if (hflip)
 			value2 |= 0x0002;
-		if (sd->vflip)
+		if (vflip)
 			value2 |= 0x0001;
 		i2c_w2(gspca_dev, 0x20, value2);
 		break;
 	case SENSOR_HV7131R:
 		i2c_r1(gspca_dev, 0x01, &value);
 		value &= ~0x03;
-		if (sd->vflip)
+		if (vflip)
 			value |= 0x01;
-		if (sd->hflip)
+		if (hflip)
 			value |= 0x02;
 		i2c_w1(gspca_dev, 0x01, value);
 		break;
@@ -2371,7 +2401,7 @@
 	{USB_DEVICE(0x0c45, 0x6248), SN9C20X(OV9655, 0x30, 0)},
 	{USB_DEVICE(0x0c45, 0x624e), SN9C20X(SOI968, 0x30,
 					     (HAS_BUTTON | LED_REVERSE))},
-	{USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, 0)},
+	{USB_DEVICE(0x0c45, 0x624f), SN9C20X(OV9650, 0x30, FLIP_DETECT)},
 	{USB_DEVICE(0x0c45, 0x6251), SN9C20X(OV9650, 0x30, 0)},
 	{USB_DEVICE(0x0c45, 0x6253), SN9C20X(OV9650, 0x30, 0)},
 	{USB_DEVICE(0x0c45, 0x6260), SN9C20X(OV7670, 0x21, 0)},