// SPDX-License-Identifier: GPL-2.0-or-later
/* Subdriver for the GL860 chip with the OV9655 sensor
* Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
* on dsd's weblog
*/
/* Sensor : OV9655 */
#include "gl860.h"
static struct validx tbl_init_at_startup[] = {
{0 x0000, 0 x0000}, {0 x0010, 0 x0010}, {0 x0008, 0 x00c0}, {0 x0001, 0 x00c1},
{0 x0001, 0 x00c2}, {0 x0020, 0 x0006}, {0 x006a, 0 x000d},
{0 x0040, 0 x0000},
};
static struct validx tbl_commmon[] = {
{0 x0041, 0 x0000}, {0 x006a, 0 x0007}, {0 x0063, 0 x0006}, {0 x006a, 0 x000d},
{0 x0000, 0 x00c0}, {0 x0010, 0 x0010}, {0 x0001, 0 x00c1}, {0 x0041, 0 x00c2},
{0 x0004, 0 x00d8}, {0 x0012, 0 x0004}, {0 x0000, 0 x0058}, {0 x0040, 0 x0000},
{0 x00f3, 0 x0006}, {0 x0058, 0 x0000}, {0 x0048, 0 x0000}, {0 x0061, 0 x0000},
};
static s32 tbl_length[] = {12 , 56 , 52 , 54 , 56 , 42 , 32 , 12 };
static u8 *tbl_640[] = {
(u8[]){
0 x00, 0 x40, 0 x07, 0 x6a, 0 x06, 0 xf3, 0 x0d, 0 x6a,
0 x10, 0 x10, 0 xc1, 0 x01
}, (u8[]){
0 x12, 0 x80, 0 x00, 0 x00, 0 x01, 0 x98, 0 x02, 0 x80,
0 x03, 0 x12, 0 x04, 0 x03, 0 x0b, 0 x57, 0 x0e, 0 x61,
0 x0f, 0 x42, 0 x11, 0 x01, 0 x12, 0 x60, 0 x13, 0 x00,
0 x14, 0 x3a, 0 x16, 0 x24, 0 x17, 0 x14, 0 x18, 0 x00,
0 x19, 0 x01, 0 x1a, 0 x3d, 0 x1e, 0 x04, 0 x24, 0 x3c,
0 x25, 0 x36, 0 x26, 0 x72, 0 x27, 0 x08, 0 x28, 0 x08,
0 x29, 0 x15, 0 x2a, 0 x00, 0 x2b, 0 x00, 0 x2c, 0 x08
}, (u8[]){
0 x32, 0 xff, 0 x33, 0 x00, 0 x34, 0 x3d, 0 x35, 0 x00,
0 x36, 0 xfa, 0 x38, 0 x72, 0 x39, 0 x57, 0 x3a, 0 x00,
0 x3b, 0 x0c, 0 x3d, 0 x99, 0 x3e, 0 x0c, 0 x3f, 0 xc1,
0 x40, 0 xc0, 0 x41, 0 x00, 0 x42, 0 xc0, 0 x43, 0 x0a,
0 x44, 0 xf0, 0 x45, 0 x46, 0 x46, 0 x62, 0 x47, 0 x2a,
0 x48, 0 x3c, 0 x4a, 0 xee, 0 x4b, 0 xe7, 0 x4c, 0 xe7,
0 x4d, 0 xe7, 0 x4e, 0 xe7
}, (u8[]){
0 x4f, 0 x98, 0 x50, 0 x98, 0 x51, 0 x00, 0 x52, 0 x28,
0 x53, 0 x70, 0 x54, 0 x98, 0 x58, 0 x1a, 0 x59, 0 x85,
0 x5a, 0 xa9, 0 x5b, 0 x64, 0 x5c, 0 x84, 0 x5d, 0 x53,
0 x5e, 0 x0e, 0 x5f, 0 xf0, 0 x60, 0 xf0, 0 x61, 0 xf0,
0 x62, 0 x00, 0 x63, 0 x00, 0 x64, 0 x02, 0 x65, 0 x20,
0 x66, 0 x00, 0 x69, 0 x0a, 0 x6b, 0 x5a, 0 x6c, 0 x04,
0 x6d, 0 x55, 0 x6e, 0 x00, 0 x6f, 0 x9d
}, (u8[]){
0 x70, 0 x15, 0 x71, 0 x78, 0 x72, 0 x00, 0 x73, 0 x00,
0 x74, 0 x3a, 0 x75, 0 x35, 0 x76, 0 x01, 0 x77, 0 x02,
0 x7a, 0 x24, 0 x7b, 0 x04, 0 x7c, 0 x07, 0 x7d, 0 x10,
0 x7e, 0 x28, 0 x7f, 0 x36, 0 x80, 0 x44, 0 x81, 0 x52,
0 x82, 0 x60, 0 x83, 0 x6c, 0 x84, 0 x78, 0 x85, 0 x8c,
0 x86, 0 x9e, 0 x87, 0 xbb, 0 x88, 0 xd2, 0 x89, 0 xe5,
0 x8a, 0 x23, 0 x8c, 0 x8d, 0 x90, 0 x7c, 0 x91, 0 x7b
}, (u8[]){
0 x9d, 0 x02, 0 x9e, 0 x02, 0 x9f, 0 x74, 0 xa0, 0 x73,
0 xa1, 0 x40, 0 xa4, 0 x50, 0 xa5, 0 x68, 0 xa6, 0 x70,
0 xa8, 0 xc1, 0 xa9, 0 xef, 0 xaa, 0 x92, 0 xab, 0 x04,
0 xac, 0 x80, 0 xad, 0 x80, 0 xae, 0 x80, 0 xaf, 0 x80,
0 xb2, 0 xf2, 0 xb3, 0 x20, 0 xb4, 0 x20, 0 xb5, 0 x00,
0 xb6, 0 xaf
}, (u8[]){
0 xbb, 0 xae, 0 xbc, 0 x4f, 0 xbd, 0 x4e, 0 xbe, 0 x6a,
0 xbf, 0 x68, 0 xc0, 0 xaa, 0 xc1, 0 xc0, 0 xc2, 0 x01,
0 xc3, 0 x4e, 0 xc6, 0 x85, 0 xc7, 0 x81, 0 xc9, 0 xe0,
0 xca, 0 xe8, 0 xcb, 0 xf0, 0 xcc, 0 xd8, 0 xcd, 0 x93
}, (u8[]){
0 xd0, 0 x01, 0 xd1, 0 x08, 0 xd2, 0 xe0, 0 xd3, 0 x01,
0 xd4, 0 x10, 0 xd5, 0 x80
}
};
static u8 *tbl_1280[] = {
(u8[]){
0 x00, 0 x40, 0 x07, 0 x6a, 0 x06, 0 xf3, 0 x0d, 0 x6a,
0 x10, 0 x10, 0 xc1, 0 x01
},
(u8[]){
0 x12, 0 x80, 0 x00, 0 x00, 0 x01, 0 x98, 0 x02, 0 x80,
0 x03, 0 x12, 0 x04, 0 x01, 0 x0b, 0 x57, 0 x0e, 0 x61,
0 x0f, 0 x42, 0 x11, 0 x00, 0 x12, 0 x00, 0 x13, 0 x00,
0 x14, 0 x3a, 0 x16, 0 x24, 0 x17, 0 x1b, 0 x18, 0 xbb,
0 x19, 0 x01, 0 x1a, 0 x81, 0 x1e, 0 x04, 0 x24, 0 x3c,
0 x25, 0 x36, 0 x26, 0 x72, 0 x27, 0 x08, 0 x28, 0 x08,
0 x29, 0 x15, 0 x2a, 0 x00, 0 x2b, 0 x00, 0 x2c, 0 x08
},
(u8[]){
0 x32, 0 xa4, 0 x33, 0 x00, 0 x34, 0 x3d, 0 x35, 0 x00,
0 x36, 0 xf8, 0 x38, 0 x72, 0 x39, 0 x57, 0 x3a, 0 x00,
0 x3b, 0 x0c, 0 x3d, 0 x99, 0 x3e, 0 x0c, 0 x3f, 0 xc2,
0 x40, 0 xc0, 0 x41, 0 x00, 0 x42, 0 xc0, 0 x43, 0 x0a,
0 x44, 0 xf0, 0 x45, 0 x46, 0 x46, 0 x62, 0 x47, 0 x2a,
0 x48, 0 x3c, 0 x4a, 0 xec, 0 x4b, 0 xe8, 0 x4c, 0 xe8,
0 x4d, 0 xe8, 0 x4e, 0 xe8
},
(u8[]){
0 x4f, 0 x98, 0 x50, 0 x98, 0 x51, 0 x00, 0 x52, 0 x28,
0 x53, 0 x70, 0 x54, 0 x98, 0 x58, 0 x1a, 0 x59, 0 x85,
0 x5a, 0 xa9, 0 x5b, 0 x64, 0 x5c, 0 x84, 0 x5d, 0 x53,
0 x5e, 0 x0e, 0 x5f, 0 xf0, 0 x60, 0 xf0, 0 x61, 0 xf0,
0 x62, 0 x00, 0 x63, 0 x00, 0 x64, 0 x02, 0 x65, 0 x20,
0 x66, 0 x00, 0 x69, 0 x02, 0 x6b, 0 x5a, 0 x6c, 0 x04,
0 x6d, 0 x55, 0 x6e, 0 x00, 0 x6f, 0 x9d
},
(u8[]){
0 x70, 0 x08, 0 x71, 0 x78, 0 x72, 0 x00, 0 x73, 0 x01,
0 x74, 0 x3a, 0 x75, 0 x35, 0 x76, 0 x01, 0 x77, 0 x02,
0 x7a, 0 x24, 0 x7b, 0 x04, 0 x7c, 0 x07, 0 x7d, 0 x10,
0 x7e, 0 x28, 0 x7f, 0 x36, 0 x80, 0 x44, 0 x81, 0 x52,
0 x82, 0 x60, 0 x83, 0 x6c, 0 x84, 0 x78, 0 x85, 0 x8c,
0 x86, 0 x9e, 0 x87, 0 xbb, 0 x88, 0 xd2, 0 x89, 0 xe5,
0 x8a, 0 x23, 0 x8c, 0 x0d, 0 x90, 0 x90, 0 x91, 0 x90
},
(u8[]){
0 x9d, 0 x02, 0 x9e, 0 x02, 0 x9f, 0 x94, 0 xa0, 0 x94,
0 xa1, 0 x01, 0 xa4, 0 x50, 0 xa5, 0 x68, 0 xa6, 0 x70,
0 xa8, 0 xc1, 0 xa9, 0 xef, 0 xaa, 0 x92, 0 xab, 0 x04,
0 xac, 0 x80, 0 xad, 0 x80, 0 xae, 0 x80, 0 xaf, 0 x80,
0 xb2, 0 xf2, 0 xb3, 0 x20, 0 xb4, 0 x20, 0 xb5, 0 x00,
0 xb6, 0 xaf
},
(u8[]){
0 xbb, 0 xae, 0 xbc, 0 x38, 0 xbd, 0 x39, 0 xbe, 0 x01,
0 xbf, 0 x01, 0 xc0, 0 xe2, 0 xc1, 0 xc0, 0 xc2, 0 x01,
0 xc3, 0 x4e, 0 xc6, 0 x85, 0 xc7, 0 x81, 0 xc9, 0 xe0,
0 xca, 0 xe8, 0 xcb, 0 xf0, 0 xcc, 0 xd8, 0 xcd, 0 x93
},
(u8[]){
0 xd0, 0 x21, 0 xd1, 0 x18, 0 xd2, 0 xe0, 0 xd3, 0 x01,
0 xd4, 0 x28, 0 xd5, 0 x00
}
};
static u8 c04[] = {0 x04};
static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02" ;
static u8 dat_post2[] = "\x10\x10\xc1\x02" ;
static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04" ;
static u8 dat_post4[] = "\x10\x02\xc1\x06" ;
static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08" ;
static u8 dat_post6[] = "\x10\x10\xc1\x05" ;
static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08" ;
static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09" ;
static struct validx tbl_init_post_alt[] = {
{0 x6032, 0 x00ff}, {0 x6032, 0 x00ff}, {0 x6032, 0 x00ff}, {0 x603c, 0 x00ff},
{0 x6003, 0 x00ff}, {0 x6032, 0 x00ff}, {0 x6032, 0 x00ff}, {0 x6001, 0 x00ff},
{0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6012, 0 x0003}, {0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6012, 0 x0003},
{0 xffff, 0 xffff},
{0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6012, 0 x0003}, {0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6012, 0 x0003},
{0 xffff, 0 xffff},
{0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6000, 0 x801e},
{0 xffff, 0 xffff},
{0 x6004, 0 x001e}, {0 x6012, 0 x0003},
};
static int ov9655_init_at_startup(struct gspca_dev *gspca_dev);
static int ov9655_configure_alt(struct gspca_dev *gspca_dev);
static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev);
static int ov9655_init_post_alt(struct gspca_dev *gspca_dev);
static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev);
static int ov9655_camera_settings(struct gspca_dev *gspca_dev);
/*==========================================================================*/
void ov9655_init_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->vcur.backlight = 0 ;
sd->vcur.brightness = 128 ;
sd->vcur.sharpness = 0 ;
sd->vcur.contrast = 0 ;
sd->vcur.gamma = 0 ;
sd->vcur.hue = 0 ;
sd->vcur.saturation = 0 ;
sd->vcur.whitebal = 0 ;
sd->vmax.backlight = 0 ;
sd->vmax.brightness = 255 ;
sd->vmax.sharpness = 0 ;
sd->vmax.contrast = 0 ;
sd->vmax.gamma = 0 ;
sd->vmax.hue = 0 + 1 ;
sd->vmax.saturation = 0 ;
sd->vmax.whitebal = 0 ;
sd->vmax.mirror = 0 ;
sd->vmax.flip = 0 ;
sd->vmax.AC50Hz = 0 ;
sd->dev_camera_settings = ov9655_camera_settings;
sd->dev_init_at_startup = ov9655_init_at_startup;
sd->dev_configure_alt = ov9655_configure_alt;
sd->dev_init_pre_alt = ov9655_init_pre_alt;
sd->dev_post_unset_alt = ov9655_post_unset_alt;
}
/*==========================================================================*/
static int ov9655_init_at_startup(struct gspca_dev *gspca_dev)
{
fetch_validx(gspca_dev, tbl_init_at_startup,
ARRAY_SIZE(tbl_init_at_startup));
fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon));
/* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL);*/
return 0 ;
}
static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
sd->vold.brightness = -1 ;
sd->vold.hue = -1 ;
fetch_validx(gspca_dev, tbl_commmon, ARRAY_SIZE(tbl_commmon));
ov9655_init_post_alt(gspca_dev);
return 0 ;
}
static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
s32 n; /* reserved for FETCH functions */
s32 i;
u8 **tbl;
ctrl_out(gspca_dev, 0 x40, 5 , 0 x0001, 0 x0000, 0 , NULL);
tbl = (reso == IMAGE_640) ? tbl_640 : tbl_1280;
ctrl_out(gspca_dev, 0 x40, 3 , 0 x0000, 0 x0200,
tbl_length[0 ], tbl[0 ]);
for (i = 1 ; i < 7 ; i++)
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200,
tbl_length[i], tbl[i]);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x0000, 0 x0200,
tbl_length[7 ], tbl[7 ]);
n = fetch_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt));
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200, 8 , dat_post1);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200, 8 , dat_post1);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_in(gspca_dev, 0 xc0, 2 , 0 x6000, 0 x801e, 1 , c04);
keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
ARRAY_SIZE(tbl_init_post_alt), n);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200, 8 , dat_post1);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x0000, 0 x0200, 4 , dat_post2);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200, 8 , dat_post3);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x0000, 0 x0200, 4 , dat_post4);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200, 8 , dat_post5);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x0000, 0 x0200, 4 , dat_post6);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200, 8 , dat_post7);
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200, 8 , dat_post8);
ov9655_camera_settings(gspca_dev);
return 0 ;
}
static int ov9655_configure_alt(struct gspca_dev *gspca_dev)
{
s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
switch (reso) {
case IMAGE_640:
gspca_dev->alt = 1 + 1 ;
break ;
default :
gspca_dev->alt = 1 + 1 ;
break ;
}
return 0 ;
}
static int ov9655_camera_settings(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 dat_bright[] = "\x04\x00\x10\x7c\xa1\x00\x00\x70" ;
s32 bright = sd->vcur.brightness;
s32 hue = sd->vcur.hue;
if (bright != sd->vold.brightness) {
sd->vold.brightness = bright;
if (bright < 0 || bright > sd->vmax.brightness)
bright = 0 ;
dat_bright[3 ] = bright;
ctrl_out(gspca_dev, 0 x40, 3 , 0 x6000, 0 x0200, 8 , dat_bright);
}
if (hue != sd->vold.hue) {
sd->vold.hue = hue;
sd->swapRB = (hue != 0 );
}
return 0 ;
}
static void ov9655_post_unset_alt(struct gspca_dev *gspca_dev)
{
ctrl_out(gspca_dev, 0 x40, 5 , 0 x0000, 0 x0000, 0 , NULL);
ctrl_out(gspca_dev, 0 x40, 1 , 0 x0061, 0 x0000, 0 , NULL);
}
Messung V0.5 in Prozent C=94 H=94 G=93