diff options
author | Jiri Kosina <jkosina@suse.cz> | 2011-04-26 10:22:15 +0200 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2011-04-26 10:22:59 +0200 |
commit | 07f9479a40cc778bc1462ada11f95b01360ae4ff (patch) | |
tree | 0676cf38df3844004bb3ebfd99dfa67a4a8998f5 /drivers/media/dvb/frontends/stv090x.c | |
parent | 9d5e6bdb3013acfb311ab407eeca0b6a6a3dedbf (diff) | |
parent | cd2e49e90f1cae7726c9a2c54488d881d7f1cd1c (diff) |
Merge branch 'master' into for-next
Fast-forwarded to current state of Linus' tree as there are patches to be
applied for files that didn't exist on the old branch.
Diffstat (limited to 'drivers/media/dvb/frontends/stv090x.c')
-rw-r--r-- | drivers/media/dvb/frontends/stv090x.c | 307 |
1 files changed, 253 insertions, 54 deletions
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 4e0fc2c8a41..52d8712411e 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -767,8 +767,12 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) * In case of any error, the lock is unlocked and exit within the * relevant operations themselves. */ - if (enable) - mutex_lock(&state->internal->tuner_lock); + if (enable) { + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 1); + else + mutex_lock(&state->internal->tuner_lock); + } reg = STV090x_READ_DEMOD(state, I2CRPT); if (enable) { @@ -784,13 +788,20 @@ static int stv090x_i2c_gate_ctrl(struct stv090x_state *state, int enable) goto err; } - if (!enable) - mutex_unlock(&state->internal->tuner_lock); + if (!enable) { + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 0); + else + mutex_unlock(&state->internal->tuner_lock); + } return 0; err: dprintk(FE_ERROR, 1, "I/O error"); - mutex_unlock(&state->internal->tuner_lock); + if (state->config->tuner_i2c_lock) + state->config->tuner_i2c_lock(&state->frontend, 0); + else + mutex_unlock(&state->internal->tuner_lock); return -1; } @@ -1413,7 +1424,7 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, CFRLOW0, 0x00) < 0) goto err; - /*enlarge the timing bandwith for Low SR*/ + /*enlarge the timing bandwidth for Low SR*/ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x68) < 0) goto err; } else { @@ -1421,17 +1432,17 @@ static int stv090x_start_search(struct stv090x_state *state) Set The carrier search up and low to auto mode */ if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0) goto err; - /*reduce the timing bandwith for high SR*/ + /*reduce the timing bandwidth for high SR*/ if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0) goto err; } } else { /* >= Cut 3 */ if (state->srate <= 5000000) { - /* enlarge the timing bandwith for Low SR */ + /* enlarge the timing bandwidth for Low SR */ STV090x_WRITE_DEMOD(state, RTCS2, 0x68); } else { - /* reduce timing bandwith for high SR */ + /* reduce timing bandwidth for high SR */ STV090x_WRITE_DEMOD(state, RTCS2, 0x44); } @@ -2471,7 +2482,7 @@ static int stv090x_sw_algo(struct stv090x_state *state) dvbs2_fly_wheel = STV090x_GETFIELD_Px(reg, FLYWHEEL_CPT_FIELD); } if (dvbs2_fly_wheel < 0xd) { - /*FALSE lock, The demod is loosing lock */ + /*FALSE lock, The demod is losing lock */ lock = 0; if (trials < 2) { if (state->internal->dev_ver >= 0x20) { @@ -2883,10 +2894,12 @@ static int stv090x_optimize_track(struct stv090x_state *state) STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0) - goto err; - if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0) - goto err; + if (state->internal->dev_ver >= 0x30) { + if (STV090x_WRITE_DEMOD(state, ACLC, 0) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, BCLC, 0) < 0) + goto err; + } if (state->frame_len == STV090x_LONG_FRAME) { reg = STV090x_READ_DEMOD(state, DMDMODCOD); modcod = STV090x_GETFIELD_Px(reg, DEMOD_MODCOD_FIELD); @@ -3189,7 +3202,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) goto err; if (STV090x_WRITE_DEMOD(state, CORRELMANT, 0x70) < 0) goto err; - if (stv090x_set_srate(state, 1000000) < 0) /* inital srate = 1Msps */ + if (stv090x_set_srate(state, 1000000) < 0) /* initial srate = 1Msps */ goto err; } else { /* known srate */ @@ -3846,6 +3859,7 @@ static int stv090x_sleep(struct dvb_frontend *fe) { struct stv090x_state *state = fe->demodulator_priv; u32 reg; + u8 full_standby = 0; if (stv090x_i2c_gate_ctrl(state, 1) < 0) goto err; @@ -3858,24 +3872,119 @@ static int stv090x_sleep(struct dvb_frontend *fe) if (stv090x_i2c_gate_ctrl(state, 0) < 0) goto err; - dprintk(FE_DEBUG, 1, "Set %s to sleep", - state->device == STV0900 ? "STV0900" : "STV0903"); + dprintk(FE_DEBUG, 1, "Set %s(%d) to sleep", + state->device == STV0900 ? "STV0900" : "STV0903", + state->demod); - reg = stv090x_read_reg(state, STV090x_SYNTCTRL); - STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01); - if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) - goto err; + mutex_lock(&state->internal->demod_lock); - reg = stv090x_read_reg(state, STV090x_TSTTNR1); - STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0); - if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) - goto err; + switch (state->demod) { + case STV090x_DEMODULATOR_0: + /* power off ADC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + STV090x_SETFIELD(reg, ADC1_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) + goto err; + /* power off DiSEqC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR2); + STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) + goto err; + + /* check whether path 2 is already sleeping, that is when + ADC2 is off */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + if (STV090x_GETFIELD(reg, ADC2_PON_FIELD) == 0) + full_standby = 1; + + /* stop clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 1); + /* ADC 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 1); + /* FEC clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 1); + /* viterbi 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 1); + /* TS clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + + case STV090x_DEMODULATOR_1: + /* power off ADC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + STV090x_SETFIELD(reg, ADC2_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) + goto err; + /* power off DiSEqC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR4); + STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 0); + if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) + goto err; + + /* check whether path 1 is already sleeping, that is when + ADC1 is off */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + if (STV090x_GETFIELD(reg, ADC1_PON_FIELD) == 0) + full_standby = 1; + + /* stop clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 1); + /* ADC 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 1); + /* FEC clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 1); + /* viterbi 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 1); + /* TS clock is shared between the two paths, only stop it + when full standby is possible */ + if (full_standby) + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 1); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + default: + dprintk(FE_ERROR, 1, "Wrong demodulator!"); + break; + } + + if (full_standby) { + /* general power off */ + reg = stv090x_read_reg(state, STV090x_SYNTCTRL); + STV090x_SETFIELD(reg, STANDBY_FIELD, 0x01); + if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) + goto err; + } + + mutex_unlock(&state->internal->demod_lock); return 0; err_gateoff: stv090x_i2c_gate_ctrl(state, 0); err: + mutex_unlock(&state->internal->demod_lock); dprintk(FE_ERROR, 1, "I/O error"); return -1; } @@ -3885,21 +3994,94 @@ static int stv090x_wakeup(struct dvb_frontend *fe) struct stv090x_state *state = fe->demodulator_priv; u32 reg; - dprintk(FE_DEBUG, 1, "Wake %s from standby", - state->device == STV0900 ? "STV0900" : "STV0903"); + dprintk(FE_DEBUG, 1, "Wake %s(%d) from standby", + state->device == STV0900 ? "STV0900" : "STV0903", + state->demod); + + mutex_lock(&state->internal->demod_lock); + /* general power on */ reg = stv090x_read_reg(state, STV090x_SYNTCTRL); STV090x_SETFIELD(reg, STANDBY_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_SYNTCTRL, reg) < 0) goto err; - reg = stv090x_read_reg(state, STV090x_TSTTNR1); - STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1); - if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) - goto err; + switch (state->demod) { + case STV090x_DEMODULATOR_0: + /* power on ADC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + STV090x_SETFIELD(reg, ADC1_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) + goto err; + /* power on DiSEqC 1 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR2); + STV090x_SETFIELD(reg, DISEQC1_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR2, reg) < 0) + goto err; + + /* activate clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT1_FIELD, 0); + /* ADC 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI1_FIELD, 0); + /* FEC clock */ + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP1_FIELD, 0); + /* viterbi 1 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, 0); + /* TS clock */ + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + case STV090x_DEMODULATOR_1: + /* power on ADC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + STV090x_SETFIELD(reg, ADC2_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) + goto err; + /* power on DiSEqC 2 */ + reg = stv090x_read_reg(state, STV090x_TSTTNR4); + STV090x_SETFIELD(reg, DISEQC2_PON_FIELD, 1); + if (stv090x_write_reg(state, STV090x_TSTTNR4, reg) < 0) + goto err; + + /* activate clocks */ + reg = stv090x_read_reg(state, STV090x_STOPCLK1); + /* packet delineator 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKPKDT2_FIELD, 0); + /* ADC 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKADCI2_FIELD, 0); + /* FEC clock */ + STV090x_SETFIELD(reg, STOP_CLKFEC_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK1, reg) < 0) + goto err; + reg = stv090x_read_reg(state, STV090x_STOPCLK2); + /* sampling 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKSAMP2_FIELD, 0); + /* viterbi 2 clock */ + STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, 0); + /* TS clock */ + STV090x_SETFIELD(reg, STOP_CLKTS_FIELD, 0); + if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) + goto err; + break; + + default: + dprintk(FE_ERROR, 1, "Wrong demodulator!"); + break; + } + + mutex_unlock(&state->internal->demod_lock); return 0; err: + mutex_unlock(&state->internal->demod_lock); dprintk(FE_ERROR, 1, "I/O error"); return -1; } @@ -4169,6 +4351,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) switch (state->config->ts1_mode) { case STV090x_TSMODE_PARALLEL_PUNCTURED: reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) @@ -4177,6 +4360,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_DVBCI: reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) @@ -4185,6 +4369,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_SERIAL_PUNCTURED: reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) @@ -4193,6 +4378,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_SERIAL_CONTINUOUS: reg = stv090x_read_reg(state, STV090x_P1_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts1_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P1_TSCFGH, reg) < 0) @@ -4206,6 +4392,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) switch (state->config->ts2_mode) { case STV090x_TSMODE_PARALLEL_PUNCTURED: reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) @@ -4214,6 +4401,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_DVBCI: reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x00); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) @@ -4222,6 +4410,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_SERIAL_PUNCTURED: reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x00); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) @@ -4230,6 +4419,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) case STV090x_TSMODE_SERIAL_CONTINUOUS: reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); + STV090x_SETFIELD_Px(reg, TSFIFO_TEIUPDATE_FIELD, state->config->ts2_tei); STV090x_SETFIELD_Px(reg, TSFIFO_SERIAL_FIELD, 0x01); STV090x_SETFIELD_Px(reg, TSFIFO_DVBCI_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) @@ -4506,16 +4696,26 @@ static int stv090x_setup(struct dvb_frontend *fe) if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0) goto err; - /* workaround for stuck DiSEqC output */ - if (config->diseqc_envelope_mode) - stv090x_send_diseqc_burst(fe, SEC_MINI_A); - return 0; err: dprintk(FE_ERROR, 1, "I/O error"); return -1; } +int stv090x_set_gpio(struct dvb_frontend *fe, u8 gpio, u8 dir, u8 value, + u8 xor_value) +{ + struct stv090x_state *state = fe->demodulator_priv; + u8 reg = 0; + + STV090x_SETFIELD(reg, GPIOx_OPD_FIELD, dir); + STV090x_SETFIELD(reg, GPIOx_CONFIG_FIELD, value); + STV090x_SETFIELD(reg, GPIOx_XOR_FIELD, xor_value); + + return stv090x_write_reg(state, STV090x_GPIOxCFG(gpio), reg); +} +EXPORT_SYMBOL(stv090x_set_gpio); + static struct dvb_frontend_ops stv090x_ops = { .info = { @@ -4580,39 +4780,35 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, state->internal = temp_int->internal; state->internal->num_used++; dprintk(FE_INFO, 1, "Found Internal Structure!"); - dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", - state->device == STV0900 ? "STV0900" : "STV0903", - demod, - state->internal->dev_ver); - return &state->frontend; } else { state->internal = kmalloc(sizeof(struct stv090x_internal), GFP_KERNEL); + if (!state->internal) + goto error; temp_int = append_internal(state->internal); + if (!temp_int) { + kfree(state->internal); + goto error; + } state->internal->num_used = 1; state->internal->mclk = 0; state->internal->dev_ver = 0; state->internal->i2c_adap = state->i2c; state->internal->i2c_addr = state->config->address; dprintk(FE_INFO, 1, "Create New Internal Structure!"); - } - mutex_init(&state->internal->demod_lock); - mutex_init(&state->internal->tuner_lock); + mutex_init(&state->internal->demod_lock); + mutex_init(&state->internal->tuner_lock); - if (stv090x_sleep(&state->frontend) < 0) { - dprintk(FE_ERROR, 1, "Error putting device to sleep"); - goto error; + if (stv090x_setup(&state->frontend) < 0) { + dprintk(FE_ERROR, 1, "Error setting up device"); + goto err_remove; + } } - if (stv090x_setup(&state->frontend) < 0) { - dprintk(FE_ERROR, 1, "Error setting up device"); - goto error; - } - if (stv090x_wakeup(&state->frontend) < 0) { - dprintk(FE_ERROR, 1, "Error waking device"); - goto error; - } + /* workaround for stuck DiSEqC output */ + if (config->diseqc_envelope_mode) + stv090x_send_diseqc_burst(&state->frontend, SEC_MINI_A); dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", state->device == STV0900 ? "STV0900" : "STV0903", @@ -4621,6 +4817,9 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, return &state->frontend; +err_remove: + remove_dev(state->internal); + kfree(state->internal); error: kfree(state); return NULL; |