diff options
Diffstat (limited to 'drivers/media/video/em28xx/em28xx-cards.c')
-rw-r--r-- | drivers/media/video/em28xx/em28xx-cards.c | 182 |
1 files changed, 171 insertions, 11 deletions
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index c43fdb9bc888..320f1f60276e 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c | |||
@@ -157,6 +157,20 @@ static struct em28xx_reg_seq evga_indtube_digital[] = { | |||
157 | { -1, -1, -1, -1}, | 157 | { -1, -1, -1, -1}, |
158 | }; | 158 | }; |
159 | 159 | ||
160 | /* Pinnacle Hybrid Pro eb1a:2881 */ | ||
161 | static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = { | ||
162 | {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10}, | ||
163 | { -1, -1, -1, -1}, | ||
164 | }; | ||
165 | |||
166 | static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { | ||
167 | {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, | ||
168 | {EM2880_R04_GPO, 0x04, 0xff, 100},/* zl10353 reset */ | ||
169 | {EM2880_R04_GPO, 0x0c, 0xff, 1}, | ||
170 | { -1, -1, -1, -1}, | ||
171 | }; | ||
172 | |||
173 | |||
160 | /* Callback for the most boards */ | 174 | /* Callback for the most boards */ |
161 | static struct em28xx_reg_seq default_tuner_gpio[] = { | 175 | static struct em28xx_reg_seq default_tuner_gpio[] = { |
162 | {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, | 176 | {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, |
@@ -191,18 +205,27 @@ static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = { | |||
191 | {EM28XX_R08_GPIO, 0xff, 0xff, 10}, | 205 | {EM28XX_R08_GPIO, 0xff, 0xff, 10}, |
192 | { -1, -1, -1, -1}, | 206 | { -1, -1, -1, -1}, |
193 | }; | 207 | }; |
208 | |||
209 | static struct em28xx_reg_seq silvercrest_reg_seq[] = { | ||
210 | {EM28XX_R08_GPIO, 0xff, 0xff, 10}, | ||
211 | {EM28XX_R08_GPIO, 0x01, 0xf7, 10}, | ||
212 | { -1, -1, -1, -1}, | ||
213 | }; | ||
214 | |||
194 | /* | 215 | /* |
195 | * Board definitions | 216 | * Board definitions |
196 | */ | 217 | */ |
197 | struct em28xx_board em28xx_boards[] = { | 218 | struct em28xx_board em28xx_boards[] = { |
198 | [EM2750_BOARD_UNKNOWN] = { | 219 | [EM2750_BOARD_UNKNOWN] = { |
199 | .name = "Unknown EM2750/EM2751 webcam grabber", | 220 | .name = "EM2710/EM2750/EM2751 webcam grabber", |
200 | .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, | 221 | .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, |
201 | .tuner_type = TUNER_ABSENT, /* This is a webcam */ | 222 | .tuner_type = TUNER_ABSENT, |
223 | .is_webcam = 1, | ||
202 | .input = { { | 224 | .input = { { |
203 | .type = EM28XX_VMUX_COMPOSITE1, | 225 | .type = EM28XX_VMUX_COMPOSITE1, |
204 | .vmux = 0, | 226 | .vmux = 0, |
205 | .amux = EM28XX_AMUX_VIDEO, | 227 | .amux = EM28XX_AMUX_VIDEO, |
228 | .gpio = silvercrest_reg_seq, | ||
206 | } }, | 229 | } }, |
207 | }, | 230 | }, |
208 | [EM2800_BOARD_UNKNOWN] = { | 231 | [EM2800_BOARD_UNKNOWN] = { |
@@ -224,13 +247,15 @@ struct em28xx_board em28xx_boards[] = { | |||
224 | [EM2820_BOARD_UNKNOWN] = { | 247 | [EM2820_BOARD_UNKNOWN] = { |
225 | .name = "Unknown EM2750/28xx video grabber", | 248 | .name = "Unknown EM2750/28xx video grabber", |
226 | .tuner_type = TUNER_ABSENT, | 249 | .tuner_type = TUNER_ABSENT, |
250 | .is_webcam = 1, /* To enable sensor probe */ | ||
227 | }, | 251 | }, |
228 | [EM2750_BOARD_DLCW_130] = { | 252 | [EM2750_BOARD_DLCW_130] = { |
229 | /* Beijing Huaqi Information Digital Technology Co., Ltd */ | 253 | /* Beijing Huaqi Information Digital Technology Co., Ltd */ |
230 | .name = "Huaqi DLCW-130", | 254 | .name = "Huaqi DLCW-130", |
231 | .valid = EM28XX_BOARD_NOT_VALIDATED, | 255 | .valid = EM28XX_BOARD_NOT_VALIDATED, |
232 | .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, | 256 | .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, |
233 | .tuner_type = TUNER_ABSENT, /* This is a webcam */ | 257 | .tuner_type = TUNER_ABSENT, |
258 | .is_webcam = 1, | ||
234 | .input = { { | 259 | .input = { { |
235 | .type = EM28XX_VMUX_COMPOSITE1, | 260 | .type = EM28XX_VMUX_COMPOSITE1, |
236 | .vmux = 0, | 261 | .vmux = 0, |
@@ -431,13 +456,25 @@ struct em28xx_board em28xx_boards[] = { | |||
431 | [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { | 456 | [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { |
432 | .name = "Videology 20K14XUSB USB2.0", | 457 | .name = "Videology 20K14XUSB USB2.0", |
433 | .valid = EM28XX_BOARD_NOT_VALIDATED, | 458 | .valid = EM28XX_BOARD_NOT_VALIDATED, |
434 | .tuner_type = TUNER_ABSENT, /* This is a webcam */ | 459 | .tuner_type = TUNER_ABSENT, |
460 | .is_webcam = 1, | ||
435 | .input = { { | 461 | .input = { { |
436 | .type = EM28XX_VMUX_COMPOSITE1, | 462 | .type = EM28XX_VMUX_COMPOSITE1, |
437 | .vmux = 0, | 463 | .vmux = 0, |
438 | .amux = EM28XX_AMUX_VIDEO, | 464 | .amux = EM28XX_AMUX_VIDEO, |
439 | } }, | 465 | } }, |
440 | }, | 466 | }, |
467 | [EM2820_BOARD_SILVERCREST_WEBCAM] = { | ||
468 | .name = "Silvercrest Webcam 1.3mpix", | ||
469 | .tuner_type = TUNER_ABSENT, | ||
470 | .is_webcam = 1, | ||
471 | .input = { { | ||
472 | .type = EM28XX_VMUX_COMPOSITE1, | ||
473 | .vmux = 0, | ||
474 | .amux = EM28XX_AMUX_VIDEO, | ||
475 | .gpio = silvercrest_reg_seq, | ||
476 | } }, | ||
477 | }, | ||
441 | [EM2821_BOARD_SUPERCOMP_USB_2] = { | 478 | [EM2821_BOARD_SUPERCOMP_USB_2] = { |
442 | .name = "Supercomp USB 2.0 TV", | 479 | .name = "Supercomp USB 2.0 TV", |
443 | .valid = EM28XX_BOARD_NOT_VALIDATED, | 480 | .valid = EM28XX_BOARD_NOT_VALIDATED, |
@@ -479,7 +516,8 @@ struct em28xx_board em28xx_boards[] = { | |||
479 | /* Beijing Huaqi Information Digital Technology Co., Ltd */ | 516 | /* Beijing Huaqi Information Digital Technology Co., Ltd */ |
480 | .name = "NetGMBH Cam", | 517 | .name = "NetGMBH Cam", |
481 | .valid = EM28XX_BOARD_NOT_VALIDATED, | 518 | .valid = EM28XX_BOARD_NOT_VALIDATED, |
482 | .tuner_type = TUNER_ABSENT, /* This is a webcam */ | 519 | .tuner_type = TUNER_ABSENT, |
520 | .is_webcam = 1, | ||
483 | .input = { { | 521 | .input = { { |
484 | .type = EM28XX_VMUX_COMPOSITE1, | 522 | .type = EM28XX_VMUX_COMPOSITE1, |
485 | .vmux = 0, | 523 | .vmux = 0, |
@@ -826,7 +864,7 @@ struct em28xx_board em28xx_boards[] = { | |||
826 | .tuner_gpio = default_tuner_gpio, | 864 | .tuner_gpio = default_tuner_gpio, |
827 | .decoder = EM28XX_TVP5150, | 865 | .decoder = EM28XX_TVP5150, |
828 | .has_dvb = 1, | 866 | .has_dvb = 1, |
829 | .dvb_gpio = default_analog, | 867 | .dvb_gpio = default_digital, |
830 | .input = { { | 868 | .input = { { |
831 | .type = EM28XX_VMUX_TELEVISION, | 869 | .type = EM28XX_VMUX_TELEVISION, |
832 | .vmux = TVP5150_COMPOSITE0, | 870 | .vmux = TVP5150_COMPOSITE0, |
@@ -1229,25 +1267,26 @@ struct em28xx_board em28xx_boards[] = { | |||
1229 | }, | 1267 | }, |
1230 | [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { | 1268 | [EM2881_BOARD_PINNACLE_HYBRID_PRO] = { |
1231 | .name = "Pinnacle Hybrid Pro", | 1269 | .name = "Pinnacle Hybrid Pro", |
1232 | .valid = EM28XX_BOARD_NOT_VALIDATED, | ||
1233 | .tuner_type = TUNER_XC2028, | 1270 | .tuner_type = TUNER_XC2028, |
1234 | .tuner_gpio = default_tuner_gpio, | 1271 | .tuner_gpio = default_tuner_gpio, |
1235 | .decoder = EM28XX_TVP5150, | 1272 | .decoder = EM28XX_TVP5150, |
1273 | .has_dvb = 1, | ||
1274 | .dvb_gpio = pinnacle_hybrid_pro_digital, | ||
1236 | .input = { { | 1275 | .input = { { |
1237 | .type = EM28XX_VMUX_TELEVISION, | 1276 | .type = EM28XX_VMUX_TELEVISION, |
1238 | .vmux = TVP5150_COMPOSITE0, | 1277 | .vmux = TVP5150_COMPOSITE0, |
1239 | .amux = EM28XX_AMUX_VIDEO, | 1278 | .amux = EM28XX_AMUX_VIDEO, |
1240 | .gpio = default_analog, | 1279 | .gpio = pinnacle_hybrid_pro_analog, |
1241 | }, { | 1280 | }, { |
1242 | .type = EM28XX_VMUX_COMPOSITE1, | 1281 | .type = EM28XX_VMUX_COMPOSITE1, |
1243 | .vmux = TVP5150_COMPOSITE1, | 1282 | .vmux = TVP5150_COMPOSITE1, |
1244 | .amux = EM28XX_AMUX_LINE_IN, | 1283 | .amux = EM28XX_AMUX_LINE_IN, |
1245 | .gpio = default_analog, | 1284 | .gpio = pinnacle_hybrid_pro_analog, |
1246 | }, { | 1285 | }, { |
1247 | .type = EM28XX_VMUX_SVIDEO, | 1286 | .type = EM28XX_VMUX_SVIDEO, |
1248 | .vmux = TVP5150_SVIDEO, | 1287 | .vmux = TVP5150_SVIDEO, |
1249 | .amux = EM28XX_AMUX_LINE_IN, | 1288 | .amux = EM28XX_AMUX_LINE_IN, |
1250 | .gpio = default_analog, | 1289 | .gpio = pinnacle_hybrid_pro_analog, |
1251 | } }, | 1290 | } }, |
1252 | }, | 1291 | }, |
1253 | [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { | 1292 | [EM2882_BOARD_PINNACLE_HYBRID_PRO] = { |
@@ -1617,6 +1656,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { | |||
1617 | {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, | 1656 | {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, |
1618 | {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, | 1657 | {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, |
1619 | {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, | 1658 | {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, |
1659 | {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, | ||
1620 | }; | 1660 | }; |
1621 | 1661 | ||
1622 | /* I2C devicelist hash table for devices with generic USB IDs */ | 1662 | /* I2C devicelist hash table for devices with generic USB IDs */ |
@@ -1639,6 +1679,11 @@ static unsigned short tvp5150_addrs[] = { | |||
1639 | I2C_CLIENT_END | 1679 | I2C_CLIENT_END |
1640 | }; | 1680 | }; |
1641 | 1681 | ||
1682 | static unsigned short mt9v011_addrs[] = { | ||
1683 | 0xba >> 1, | ||
1684 | I2C_CLIENT_END | ||
1685 | }; | ||
1686 | |||
1642 | static unsigned short msp3400_addrs[] = { | 1687 | static unsigned short msp3400_addrs[] = { |
1643 | 0x80 >> 1, | 1688 | 0x80 >> 1, |
1644 | 0x88 >> 1, | 1689 | 0x88 >> 1, |
@@ -1678,6 +1723,91 @@ static inline void em28xx_set_model(struct em28xx *dev) | |||
1678 | EM28XX_I2C_FREQ_100_KHZ; | 1723 | EM28XX_I2C_FREQ_100_KHZ; |
1679 | } | 1724 | } |
1680 | 1725 | ||
1726 | /* FIXME: Should be replaced by a proper mt9m001 driver */ | ||
1727 | static int em28xx_initialize_mt9m001(struct em28xx *dev) | ||
1728 | { | ||
1729 | int i; | ||
1730 | unsigned char regs[][3] = { | ||
1731 | { 0x0d, 0x00, 0x01, }, | ||
1732 | { 0x0d, 0x00, 0x00, }, | ||
1733 | { 0x04, 0x05, 0x00, }, /* hres = 1280 */ | ||
1734 | { 0x03, 0x04, 0x00, }, /* vres = 1024 */ | ||
1735 | { 0x20, 0x11, 0x00, }, | ||
1736 | { 0x06, 0x00, 0x10, }, | ||
1737 | { 0x2b, 0x00, 0x24, }, | ||
1738 | { 0x2e, 0x00, 0x24, }, | ||
1739 | { 0x35, 0x00, 0x24, }, | ||
1740 | { 0x2d, 0x00, 0x20, }, | ||
1741 | { 0x2c, 0x00, 0x20, }, | ||
1742 | { 0x09, 0x0a, 0xd4, }, | ||
1743 | { 0x35, 0x00, 0x57, }, | ||
1744 | }; | ||
1745 | |||
1746 | for (i = 0; i < ARRAY_SIZE(regs); i++) | ||
1747 | i2c_master_send(&dev->i2c_client, ®s[i][0], 3); | ||
1748 | |||
1749 | return 0; | ||
1750 | } | ||
1751 | |||
1752 | /* HINT method: webcam I2C chips | ||
1753 | * | ||
1754 | * This method work for webcams with Micron sensors | ||
1755 | */ | ||
1756 | static int em28xx_hint_sensor(struct em28xx *dev) | ||
1757 | { | ||
1758 | int rc; | ||
1759 | char *sensor_name; | ||
1760 | unsigned char cmd; | ||
1761 | __be16 version_be; | ||
1762 | u16 version; | ||
1763 | |||
1764 | dev->i2c_client.addr = 0xba >> 1; | ||
1765 | cmd = 0; | ||
1766 | i2c_master_send(&dev->i2c_client, &cmd, 1); | ||
1767 | rc = i2c_master_recv(&dev->i2c_client, (char *)&version_be, 2); | ||
1768 | if (rc != 2) | ||
1769 | return -EINVAL; | ||
1770 | |||
1771 | version = be16_to_cpu(version_be); | ||
1772 | |||
1773 | switch (version) { | ||
1774 | case 0x8243: /* mt9v011 640x480 1.3 Mpix sensor */ | ||
1775 | dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; | ||
1776 | sensor_name = "mt9v011"; | ||
1777 | dev->em28xx_sensor = EM28XX_MT9V011; | ||
1778 | dev->sensor_xres = 640; | ||
1779 | dev->sensor_yres = 480; | ||
1780 | dev->sensor_xtal = 6300000; | ||
1781 | |||
1782 | /* probably means GRGB 16 bit bayer */ | ||
1783 | dev->vinmode = 0x0d; | ||
1784 | dev->vinctl = 0x00; | ||
1785 | |||
1786 | break; | ||
1787 | case 0x8431: | ||
1788 | dev->model = EM2750_BOARD_UNKNOWN; | ||
1789 | sensor_name = "mt9m001"; | ||
1790 | dev->em28xx_sensor = EM28XX_MT9M001; | ||
1791 | em28xx_initialize_mt9m001(dev); | ||
1792 | dev->sensor_xres = 1280; | ||
1793 | dev->sensor_yres = 1024; | ||
1794 | |||
1795 | /* probably means BGGR 16 bit bayer */ | ||
1796 | dev->vinmode = 0x0c; | ||
1797 | dev->vinctl = 0x00; | ||
1798 | |||
1799 | break; | ||
1800 | default: | ||
1801 | printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version)); | ||
1802 | return -EINVAL; | ||
1803 | } | ||
1804 | |||
1805 | em28xx_errdev("Sensor is %s, using model %s entry.\n", | ||
1806 | sensor_name, em28xx_boards[dev->model].name); | ||
1807 | |||
1808 | return 0; | ||
1809 | } | ||
1810 | |||
1681 | /* Since em28xx_pre_card_setup() requires a proper dev->model, | 1811 | /* Since em28xx_pre_card_setup() requires a proper dev->model, |
1682 | * this won't work for boards with generic PCI IDs | 1812 | * this won't work for boards with generic PCI IDs |
1683 | */ | 1813 | */ |
@@ -1706,7 +1836,7 @@ void em28xx_pre_card_setup(struct em28xx *dev) | |||
1706 | em28xx_info("chip ID is em2750\n"); | 1836 | em28xx_info("chip ID is em2750\n"); |
1707 | break; | 1837 | break; |
1708 | case CHIP_ID_EM2820: | 1838 | case CHIP_ID_EM2820: |
1709 | em28xx_info("chip ID is em2820\n"); | 1839 | em28xx_info("chip ID is em2710 or em2820\n"); |
1710 | break; | 1840 | break; |
1711 | case CHIP_ID_EM2840: | 1841 | case CHIP_ID_EM2840: |
1712 | em28xx_info("chip ID is em2840\n"); | 1842 | em28xx_info("chip ID is em2840\n"); |
@@ -1860,6 +1990,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) | |||
1860 | ctl->demod = XC3028_FE_ZARLINK456; | 1990 | ctl->demod = XC3028_FE_ZARLINK456; |
1861 | break; | 1991 | break; |
1862 | case EM2880_BOARD_TERRATEC_HYBRID_XS: | 1992 | case EM2880_BOARD_TERRATEC_HYBRID_XS: |
1993 | case EM2881_BOARD_PINNACLE_HYBRID_PRO: | ||
1863 | ctl->demod = XC3028_FE_ZARLINK456; | 1994 | ctl->demod = XC3028_FE_ZARLINK456; |
1864 | break; | 1995 | break; |
1865 | case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: | 1996 | case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: |
@@ -2156,8 +2287,13 @@ void em28xx_card_setup(struct em28xx *dev) | |||
2156 | em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, | 2287 | em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, |
2157 | so make the call now so the analog GPIOs are set properly | 2288 | so make the call now so the analog GPIOs are set properly |
2158 | before probing the i2c bus. */ | 2289 | before probing the i2c bus. */ |
2290 | em28xx_gpio_set(dev, dev->board.tuner_gpio); | ||
2159 | em28xx_set_mode(dev, EM28XX_ANALOG_MODE); | 2291 | em28xx_set_mode(dev, EM28XX_ANALOG_MODE); |
2160 | break; | 2292 | break; |
2293 | case EM2820_BOARD_SILVERCREST_WEBCAM: | ||
2294 | /* FIXME: need to document the registers bellow */ | ||
2295 | em28xx_write_reg(dev, 0x0d, 0x42); | ||
2296 | em28xx_write_reg(dev, 0x13, 0x08); | ||
2161 | } | 2297 | } |
2162 | 2298 | ||
2163 | if (dev->board.has_snapshot_button) | 2299 | if (dev->board.has_snapshot_button) |
@@ -2189,6 +2325,15 @@ void em28xx_card_setup(struct em28xx *dev) | |||
2189 | v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, | 2325 | v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, |
2190 | "tvp5150", "tvp5150", tvp5150_addrs); | 2326 | "tvp5150", "tvp5150", tvp5150_addrs); |
2191 | 2327 | ||
2328 | if (dev->em28xx_sensor == EM28XX_MT9V011) { | ||
2329 | struct v4l2_subdev *sd; | ||
2330 | |||
2331 | sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, | ||
2332 | &dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs); | ||
2333 | v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); | ||
2334 | } | ||
2335 | |||
2336 | |||
2192 | if (dev->board.adecoder == EM28XX_TVAUDIO) | 2337 | if (dev->board.adecoder == EM28XX_TVAUDIO) |
2193 | v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, | 2338 | v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, |
2194 | "tvaudio", "tvaudio", dev->board.tvaudio_addr); | 2339 | "tvaudio", "tvaudio", dev->board.tvaudio_addr); |
@@ -2333,6 +2478,20 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, | |||
2333 | return errCode; | 2478 | return errCode; |
2334 | } | 2479 | } |
2335 | 2480 | ||
2481 | /* | ||
2482 | * Default format, used for tvp5150 or saa711x output formats | ||
2483 | */ | ||
2484 | dev->vinmode = 0x10; | ||
2485 | dev->vinctl = 0x11; | ||
2486 | |||
2487 | /* | ||
2488 | * If the device can be a webcam, seek for a sensor. | ||
2489 | * If sensor is not found, then it isn't a webcam. | ||
2490 | */ | ||
2491 | if (dev->board.is_webcam) | ||
2492 | if (em28xx_hint_sensor(dev) < 0) | ||
2493 | dev->board.is_webcam = 0; | ||
2494 | |||
2336 | /* Do board specific init and eeprom reading */ | 2495 | /* Do board specific init and eeprom reading */ |
2337 | em28xx_card_setup(dev); | 2496 | em28xx_card_setup(dev); |
2338 | 2497 | ||
@@ -2573,6 +2732,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, | |||
2573 | retval = em28xx_init_dev(&dev, udev, interface, nr); | 2732 | retval = em28xx_init_dev(&dev, udev, interface, nr); |
2574 | if (retval) { | 2733 | if (retval) { |
2575 | em28xx_devused &= ~(1<<dev->devno); | 2734 | em28xx_devused &= ~(1<<dev->devno); |
2735 | mutex_unlock(&dev->lock); | ||
2576 | kfree(dev); | 2736 | kfree(dev); |
2577 | goto err; | 2737 | goto err; |
2578 | } | 2738 | } |